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