vfs: Remove KFS, blockdev and devfs
[akaros.git] / kern / src / net / ipaux.c
1 /* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
2  * Portions Copyright © 1997-1999 Vita Nuova Limited
3  * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
4  *                                (www.vitanuova.com)
5  * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
6  *
7  * Modified for the Akaros operating system:
8  * Copyright (c) 2013-2014 The Regents of the University of California
9  * Copyright (c) 2013-2015 Google Inc.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a copy
12  * of this software and associated documentation files (the "Software"), to deal
13  * in the Software without restriction, including without limitation the rights
14  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15  * copies of the Software, and to permit persons to whom the Software is
16  * furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE. */
28
29 #include <vfs.h>
30 #include <slab.h>
31 #include <kmalloc.h>
32 #include <kref.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include <assert.h>
36 #include <error.h>
37 #include <cpio.h>
38 #include <pmap.h>
39 #include <smp.h>
40 #include <net/ip.h>
41 #include <endian.h>
42
43 /*
44  *  well known IP addresses
45  */
46 uint8_t IPv4_loopback[IPaddrlen] = {
47         0, 0, 0, 0,
48         0, 0, 0, 0,
49         0, 0, 0xff, 0xff,
50         0x7f, 0x00, 0x00, 0x01
51 };
52
53 uint8_t IPv4_zeroes[IPaddrlen] = {
54         0, 0, 0, 0,
55         0, 0, 0, 0,
56         0, 0, 0xff, 0xff,
57         0x00, 0x00, 0x00, 0x00
58 };
59
60 uint8_t IPv4bcast[IPaddrlen] = {
61         0, 0, 0, 0,
62         0, 0, 0, 0,
63         0, 0, 0xff, 0xff,
64         0xff, 0xff, 0xff, 0xff
65 };
66
67 uint8_t IPv4allsys[IPaddrlen] = {
68         0, 0, 0, 0,
69         0, 0, 0, 0,
70         0, 0, 0xff, 0xff,
71         0xe0, 0, 0, 0x01
72 };
73
74 uint8_t IPv4allrouter[IPaddrlen] = {
75         0, 0, 0, 0,
76         0, 0, 0, 0,
77         0, 0, 0xff, 0xff,
78         0xe0, 0, 0, 0x02
79 };
80
81 uint8_t IPallbits[IPaddrlen] = {
82         0xff, 0xff, 0xff, 0xff,
83         0xff, 0xff, 0xff, 0xff,
84         0xff, 0xff, 0xff, 0xff,
85         0xff, 0xff, 0xff, 0xff
86 };
87
88 uint8_t IPnoaddr[IPaddrlen];
89
90 /*
91  *  prefix of all v4 addresses
92  */
93 uint8_t v4prefix[IPaddrlen] = {
94         0, 0, 0, 0,
95         0, 0, 0, 0,
96         0, 0, 0xff, 0xff,
97         0, 0, 0, 0
98 };
99
100 char *v6hdrtypes[Maxhdrtype] = {
101         [HBH] "HopbyHop",
102         [ICMP] "ICMP",
103         [IGMP] "IGMP",
104         [GGP] "GGP",
105         [IPINIP] "IP",
106         [ST] "ST",
107         [TCP] "TCP",
108         [UDP] "UDP",
109         [ISO_TP4] "ISO_TP4",
110         [RH] "Routinghdr",
111         [FH] "Fraghdr",
112         [IDRP] "IDRP",
113         [RSVP] "RSVP",
114         [AH] "Authhdr",
115         [ESP] "ESP",
116         [ICMPv6] "ICMPv6",
117         [NNH] "Nonexthdr",
118         [ISO_IP] "ISO_IP",
119         [IGRP] "IGRP",
120         [OSPF] "OSPF",
121 };
122
123 /*
124  *  well known IPv6 addresses
125  */
126 uint8_t v6Unspecified[IPaddrlen] = {
127         0, 0, 0, 0,
128         0, 0, 0, 0,
129         0, 0, 0, 0,
130         0, 0, 0, 0
131 };
132
133 uint8_t v6loopback[IPaddrlen] = {
134         0, 0, 0, 0,
135         0, 0, 0, 0,
136         0, 0, 0, 0,
137         0, 0, 0, 0x01
138 };
139
140 uint8_t v6linklocal[IPaddrlen] = {
141         0xfe, 0x80, 0, 0,
142         0, 0, 0, 0,
143         0, 0, 0, 0,
144         0, 0, 0, 0
145 };
146
147 uint8_t v6linklocalmask[IPaddrlen] = {
148         0xff, 0xff, 0xff, 0xff,
149         0xff, 0xff, 0xff, 0xff,
150         0, 0, 0, 0,
151         0, 0, 0, 0
152 };
153
154 int v6llpreflen = 8;                    // link-local prefix length
155 uint8_t v6sitelocal[IPaddrlen] = {
156         0xfe, 0xc0, 0, 0,
157         0, 0, 0, 0,
158         0, 0, 0, 0,
159         0, 0, 0, 0
160 };
161
162 uint8_t v6sitelocalmask[IPaddrlen] = {
163         0xff, 0xff, 0xff, 0xff,
164         0xff, 0xff, 0xff, 0xff,
165         0, 0, 0, 0,
166         0, 0, 0, 0
167 };
168
169 int v6slpreflen = 6;                    // site-local prefix length
170 uint8_t v6glunicast[IPaddrlen] = {
171         0x08, 0, 0, 0,
172         0, 0, 0, 0,
173         0, 0, 0, 0,
174         0, 0, 0, 0
175 };
176
177 uint8_t v6multicast[IPaddrlen] = {
178         0xff, 0, 0, 0,
179         0, 0, 0, 0,
180         0, 0, 0, 0,
181         0, 0, 0, 0
182 };
183
184 uint8_t v6multicastmask[IPaddrlen] = {
185         0xff, 0, 0, 0,
186         0, 0, 0, 0,
187         0, 0, 0, 0,
188         0, 0, 0, 0
189 };
190
191 int v6mcpreflen = 1;                    // multicast prefix length
192 uint8_t v6allnodesN[IPaddrlen] = {
193         0xff, 0x01, 0, 0,
194         0, 0, 0, 0,
195         0, 0, 0, 0,
196         0, 0, 0, 0x01
197 };
198
199 uint8_t v6allnodesNmask[IPaddrlen] = {
200         0xff, 0xff, 0, 0,
201         0, 0, 0, 0,
202         0, 0, 0, 0,
203         0, 0, 0, 0
204 };
205
206 int v6aNpreflen = 2;                    // all nodes (N) prefix
207 uint8_t v6allnodesL[IPaddrlen] = {
208         0xff, 0x02, 0, 0,
209         0, 0, 0, 0,
210         0, 0, 0, 0,
211         0, 0, 0, 0x01
212 };
213
214 uint8_t v6allnodesLmask[IPaddrlen] = {
215         0xff, 0xff, 0, 0,
216         0, 0, 0, 0,
217         0, 0, 0, 0,
218         0, 0, 0, 0
219 };
220
221 int v6aLpreflen = 2;                    // all nodes (L) prefix
222 uint8_t v6allroutersN[IPaddrlen] = {
223         0xff, 0x01, 0, 0,
224         0, 0, 0, 0,
225         0, 0, 0, 0,
226         0, 0, 0, 0x02
227 };
228
229 uint8_t v6allroutersL[IPaddrlen] = {
230         0xff, 0x02, 0, 0,
231         0, 0, 0, 0,
232         0, 0, 0, 0,
233         0, 0, 0, 0x02
234 };
235
236 uint8_t v6allroutersS[IPaddrlen] = {
237         0xff, 0x05, 0, 0,
238         0, 0, 0, 0,
239         0, 0, 0, 0,
240         0, 0, 0, 0x02
241 };
242
243 uint8_t v6solicitednode[IPaddrlen] = {
244         0xff, 0x02, 0, 0,
245         0, 0, 0, 0,
246         0, 0, 0, 0x01,
247         0xff, 0, 0, 0
248 };
249
250 uint8_t v6solicitednodemask[IPaddrlen] = {
251         0xff, 0xff, 0xff, 0xff,
252         0xff, 0xff, 0xff, 0xff,
253         0xff, 0xff, 0xff, 0xff,
254         0xff, 0x0, 0x0, 0x0
255 };
256
257 int v6snpreflen = 13;
258
259 uint16_t ptclcsum_one(struct block *bp, int offset, int len)
260 {
261         uint8_t *addr;
262         uint32_t losum, hisum;
263         uint16_t csum;
264         int odd, blocklen, x, i, boff;
265         struct extra_bdata *ebd;
266
267         hisum = 0;
268         losum = 0;
269         odd = 0;
270
271         if (offset < BHLEN(bp)) {
272                 x = MIN(len, BHLEN(bp) - offset);
273                 odd = (odd + x) & 1;
274                 addr = bp->rp + offset;
275                 losum = ptclbsum(addr, x);
276                 len -= x;
277                 offset = 0;
278         } else {
279                 offset -= BHLEN(bp);
280         }
281         for (int i = 0; (i < bp->nr_extra_bufs) && len; i++) {
282                 ebd = &bp->extra_data[i];
283                 boff = MIN(offset, ebd->len);
284                 if (offset) {
285                         offset -= boff;
286                         if (offset)
287                                 continue;
288                 }
289                 x = MIN(len, ebd->len - boff);
290                 addr = (void *)(ebd->base + ebd->off);
291                 if (odd)
292                         hisum += ptclbsum(addr, x);
293                 else
294                         losum += ptclbsum(addr, x);
295                 odd = (odd + x) & 1;
296                 len -= x;
297         }
298         losum += hisum >> 8;
299         losum += (hisum & 0xff) << 8;
300         while ((csum = losum >> 16) != 0)
301                 losum = csum + (losum & 0xffff);
302
303         return losum & 0xffff;
304 }
305
306 uint16_t ptclcsum(struct block *bp, int offset, int len)
307 {
308         uint8_t *addr;
309         uint32_t losum, hisum;
310         uint16_t csum;
311         int odd, blocklen, x;
312
313         /* Correct to front of data area */
314         while (bp != NULL && offset && offset >= BLEN(bp)) {
315                 offset -= BLEN(bp);
316                 bp = bp->next;
317         }
318         if (bp == NULL)
319                 return 0;
320
321         addr = bp->rp + offset;
322         blocklen = BLEN(bp) - offset;
323
324         if (bp->next == NULL && bp->extra_len == 0) {
325                 if (blocklen < len)
326                         len = blocklen;
327                 return ~ptclbsum(addr, len) & 0xffff;
328         }
329
330         losum = 0;
331         hisum = 0;
332
333         odd = 0;
334         while (len) {
335                 x = MIN(blocklen, len);
336                 csum = ptclcsum_one(bp, offset, x);
337                 if (odd)
338                         hisum += csum;
339                 else
340                         losum += csum;
341                 odd = (odd + x) & 1;
342                 len -= x;
343
344                 bp = bp->next;
345                 if (bp == NULL)
346                         break;
347                 blocklen = BLEN(bp);
348                 offset = 0;
349         }
350
351         losum += hisum >> 8;
352         losum += (hisum & 0xff) << 8;
353         while ((csum = losum >> 16) != 0)
354                 losum = csum + (losum & 0xffff);
355
356         return ~losum & 0xffff;
357 }
358
359 enum {
360         Isprefix = 16,
361 };
362
363 static uint8_t prefixvals[256] = {
364         [0x00] 0 | Isprefix,
365         [0x80] 1 | Isprefix,
366         [0xC0] 2 | Isprefix,
367         [0xE0] 3 | Isprefix,
368         [0xF0] 4 | Isprefix,
369         [0xF8] 5 | Isprefix,
370         [0xFC] 6 | Isprefix,
371         [0xFE] 7 | Isprefix,
372         [0xFF] 8 | Isprefix,
373 };
374
375 #define CLASS(p) ((*( uint8_t *)(p))>>6)
376
377 extern char *v4parseip(uint8_t * to, char *from)
378 {
379         int i;
380         char *p;
381
382         p = from;
383         for (i = 0; i < 4 && *p; i++) {
384                 to[i] = strtoul(p, &p, 0);
385                 if (*p == '.')
386                         p++;
387         }
388         switch (CLASS(to)) {
389                 case 0: /* class A - 1 uint8_t net */
390                 case 1:
391                         if (i == 3) {
392                                 to[3] = to[2];
393                                 to[2] = to[1];
394                                 to[1] = 0;
395                         } else if (i == 2) {
396                                 to[3] = to[1];
397                                 to[1] = 0;
398                         }
399                         break;
400                 case 2: /* class B - 2 uint8_t net */
401                         if (i == 3) {
402                                 to[3] = to[2];
403                                 to[2] = 0;
404                         }
405                         break;
406         }
407         return p;
408 }
409
410 int isv4(uint8_t * ip)
411 {
412         unsigned short *ips = (unsigned short *)ip;
413         return 0xffff == ips[5];
414 }
415
416 /*
417  *  the following routines are unrolled with no memset's to speed
418  *  up the usual case.  They assume IP addresses are naturally aligned.
419  */
420 void v4tov6(uint8_t * v6, uint8_t * v4)
421 {
422         uint32_t *v6p = (uint32_t *)v6;
423         uint32_t *v4p = (uint32_t *)v4;
424
425         v6p[0] = 0;
426         v6p[1] = 0;
427         v6p[2] = (unsigned int)PP_NTOHL(0xffff);
428         v6p[3] = v4p[0];
429 }
430
431 int v6tov4(uint8_t * v4, uint8_t * v6)
432 {
433
434         uint32_t *v6p = (uint32_t *)v6;
435         uint32_t *v4p = (uint32_t *)v4;
436
437         if (v6p[0] == 0 && v6p[1] == 0 &&
438             v6p[2] == (unsigned int)PP_NTOHL(0xffff)) {
439                 v4p[0] = v6p[3];
440                 return 0;
441         } else {
442                 v4p[0] = 0;
443                 return -1;
444         }
445 }
446
447 uint32_t parseip(uint8_t * to, char *from)
448 {
449         int i, elipsis = 0, v4 = 1;
450         uint32_t x;
451         char *p, *op;
452
453         memset(to, 0, IPaddrlen);
454         p = from;
455         for (i = 0; i < 16 && *p; i += 2) {
456                 op = p;
457                 x = strtoul(p, &p, 16);
458                 if (*p == '.' || (*p == 0 && i == 0)) {
459                         p = v4parseip(to + i, op);
460                         i += 4;
461                         break;
462                 } else {
463                         to[i] = x >> 8;
464                         to[i + 1] = x;
465                 }
466                 if (*p == ':') {
467                         v4 = 0;
468                         if (*++p == ':') {
469                                 elipsis = i + 2;
470                                 p++;
471                         }
472                 }
473         }
474         if (i < 16) {
475                 memmove(&to[elipsis + 16 - i], &to[elipsis], i - elipsis);
476                 memset(&to[elipsis], 0, 16 - i);
477         }
478         if (v4) {
479                 to[10] = to[11] = 0xff;
480                 return nhgetl(to + 12);
481         } else
482                 return 6;
483 }
484
485 /*
486  *  hack to allow ip v4 masks to be entered in the old
487  *  style
488  */
489 uint32_t parseipmask(uint8_t * to, char *from)
490 {
491         uint32_t x;
492         int i;
493         uint8_t *p;
494
495         if (*from == '/') {
496                 /* as a number of prefix bits */
497                 i = atoi(from + 1);
498                 if (i < 0)
499                         i = 0;
500                 if (i > 128)
501                         i = 128;
502                 memset(to, 0, IPaddrlen);
503                 for (p = to; i >= 8; i -= 8)
504                         *p++ = 0xff;
505                 if (i > 0)
506                         *p = ~((1 << (8 - i)) - 1);
507                 x = nhgetl(to + IPv4off);
508         } else {
509                 /* as a straight bit mask */
510                 x = parseip(to, from);
511                 if (memcmp(to, v4prefix, IPv4off) == 0)
512                         memset(to, 0xff, IPv4off);
513         }
514         return x;
515 }
516
517 void maskip(uint8_t * from, uint8_t * mask, uint8_t * to)
518 {
519         int i;
520
521         for (i = 0; i < IPaddrlen; i++)
522                 to[i] = from[i] & mask[i];
523 }
524
525 uint8_t classmask[4][16] = {
526         {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
527          0xff, 0x00, 0x00, 0x00}
528         ,
529         {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
530          0xff, 0x00, 0x00, 0x00}
531         ,
532         {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
533          0xff, 0xff, 0x00, 0x00}
534         ,
535
536         {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
537          0xff, 0xff, 0xff, 0x00}
538         ,
539 };
540
541 uint8_t *defmask(uint8_t * ip)
542 {
543         if (isv4(ip))
544                 return classmask[ip[IPv4off] >> 6];
545         else {
546                 if (ipcmp(ip, v6loopback) == 0)
547                         return IPallbits;
548                 else if (memcmp(ip, v6linklocal, v6llpreflen) == 0)
549                         return v6linklocalmask;
550                 else if (memcmp(ip, v6sitelocal, v6slpreflen) == 0)
551                         return v6sitelocalmask;
552                 else if (memcmp(ip, v6solicitednode, v6snpreflen) == 0)
553                         return v6solicitednodemask;
554                 else if (memcmp(ip, v6multicast, v6mcpreflen) == 0)
555                         return v6multicastmask;
556                 return IPallbits;
557         }
558 }
559
560 void ipv62smcast(uint8_t * smcast, uint8_t * a)
561 {
562         assert(IPaddrlen == 16);
563         memmove(smcast, v6solicitednode, IPaddrlen);
564         smcast[13] = a[13];
565         smcast[14] = a[14];
566         smcast[15] = a[15];
567 }
568
569 /*
570  *  parse a hex mac address
571  */
572 int parsemac(uint8_t * to, char *from, int len)
573 {
574         char nip[4];
575         char *p;
576         int i;
577
578         p = from;
579         memset(to, 0, len);
580         for (i = 0; i < len; i++) {
581                 if (p[0] == '\0' || p[1] == '\0')
582                         break;
583
584                 nip[0] = p[0];
585                 nip[1] = p[1];
586                 nip[2] = '\0';
587                 p += 2;
588
589                 to[i] = strtoul(nip, 0, 16);
590                 if (*p == ':')
591                         p++;
592         }
593         return i;
594 }
595
596 /*
597  *  hashing tcp, udp, ... connections
598  *  gcc weirdness: it gave a bogus result until ron split the %= out.
599  */
600 uint32_t iphash(uint8_t * sa, uint16_t sp, uint8_t * da, uint16_t dp)
601 {
602         uint32_t ret;
603         ret = (sa[IPaddrlen - 1] << 24) ^ (sp << 16) ^ (da[IPaddrlen - 1] << 8)
604                 ^ dp;
605         ret %= Nhash;
606         return ret;
607 }
608
609 void iphtadd(struct Ipht *ht, struct conv *c)
610 {
611         uint32_t hv;
612         struct Iphash *h;
613
614         hv = iphash(c->raddr, c->rport, c->laddr, c->lport);
615         h = kzmalloc(sizeof(*h), 0);
616         if (ipcmp(c->raddr, IPnoaddr) != 0)
617                 h->match = IPmatchexact;
618         else {
619                 if (ipcmp(c->laddr, IPnoaddr) != 0) {
620                         if (c->lport == 0)
621                                 h->match = IPmatchaddr;
622                         else
623                                 h->match = IPmatchpa;
624                 } else {
625                         if (c->lport == 0)
626                                 h->match = IPmatchany;
627                         else
628                                 h->match = IPmatchport;
629                 }
630         }
631         h->c = c;
632
633         spin_lock(&ht->lock);
634         h->next = ht->tab[hv];
635         ht->tab[hv] = h;
636         spin_unlock(&ht->lock);
637 }
638
639 void iphtrem(struct Ipht *ht, struct conv *c)
640 {
641         uint32_t hv;
642         struct Iphash **l, *h;
643
644         hv = iphash(c->raddr, c->rport, c->laddr, c->lport);
645         spin_lock(&ht->lock);
646         for (l = &ht->tab[hv]; (*l) != NULL; l = &(*l)->next)
647                 if ((*l)->c == c) {
648                         h = *l;
649                         (*l) = h->next;
650                         kfree(h);
651                         break;
652                 }
653         spin_unlock(&ht->lock);
654 }
655
656 /* look for a matching conversation with the following precedence
657  *      connected && raddr,rport,laddr,lport
658  *      announced && laddr,lport
659  *      announced && *,lport
660  *      announced && laddr,*
661  *      announced && *,*
662  */
663 struct conv *iphtlook(struct Ipht *ht, uint8_t * sa, uint16_t sp, uint8_t * da,
664                                           uint16_t dp)
665 {
666         uint32_t hv;
667         struct Iphash *h;
668         struct conv *c;
669
670         /* exact 4 pair match (connection) */
671         hv = iphash(sa, sp, da, dp);
672         spin_lock(&ht->lock);
673         for (h = ht->tab[hv]; h != NULL; h = h->next) {
674                 if (h->match != IPmatchexact)
675                         continue;
676                 c = h->c;
677                 if (sp == c->rport && dp == c->lport
678                         && ipcmp(sa, c->raddr) == 0 && ipcmp(da, c->laddr) == 0) {
679                         spin_unlock(&ht->lock);
680                         return c;
681                 }
682         }
683
684         /* match local address and port */
685         hv = iphash(IPnoaddr, 0, da, dp);
686         for (h = ht->tab[hv]; h != NULL; h = h->next) {
687                 if (h->match != IPmatchpa)
688                         continue;
689                 c = h->c;
690                 if (dp == c->lport && ipcmp(da, c->laddr) == 0) {
691                         spin_unlock(&ht->lock);
692                         return c;
693                 }
694         }
695
696         /* match just port */
697         hv = iphash(IPnoaddr, 0, IPnoaddr, dp);
698         for (h = ht->tab[hv]; h != NULL; h = h->next) {
699                 if (h->match != IPmatchport)
700                         continue;
701                 c = h->c;
702                 if (dp == c->lport) {
703                         spin_unlock(&ht->lock);
704                         return c;
705                 }
706         }
707
708         /* match local address */
709         hv = iphash(IPnoaddr, 0, da, 0);
710         for (h = ht->tab[hv]; h != NULL; h = h->next) {
711                 if (h->match != IPmatchaddr)
712                         continue;
713                 c = h->c;
714                 if (ipcmp(da, c->laddr) == 0) {
715                         spin_unlock(&ht->lock);
716                         return c;
717                 }
718         }
719
720         /* look for something that matches anything */
721         hv = iphash(IPnoaddr, 0, IPnoaddr, 0);
722         for (h = ht->tab[hv]; h != NULL; h = h->next) {
723                 if (h->match != IPmatchany)
724                         continue;
725                 c = h->c;
726                 spin_unlock(&ht->lock);
727                 return c;
728         }
729         spin_unlock(&ht->lock);
730         return NULL;
731 }
732
733 void dump_ipht(struct Ipht *ht)
734 {
735         struct Iphash *h;
736         struct conv *c;
737
738         spin_lock(&ht->lock);
739         for (int i = 0; i < Nipht; i++) {
740                 for (h = ht->tab[i]; h != NULL; h = h->next) {
741                         c = h->c;
742                         printk("Conv proto %s, idx %d: local %I:%d, remote %I:%d\n",
743                                c->p->name, c->x, c->laddr, c->lport, c->raddr, c->rport);
744                 }
745         }
746         spin_unlock(&ht->lock);
747 }