Assume natural alignment for IP & ether addrs
[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(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;
225
226         /* Correct to front of data area */
227         while (bp != NULL && offset && offset >= BLEN(bp)) {
228                 offset -= BLEN(bp);
229                 bp = bp->next;
230         }
231         if (bp == NULL)
232                 return 0;
233
234         addr = bp->rp + offset;
235         blocklen = BLEN(bp) - offset;
236
237         if (bp->next == NULL) {
238                 if (blocklen < len)
239                         len = blocklen;
240                 return ~ptclbsum(addr, len) & 0xffff;
241         }
242
243         losum = 0;
244         hisum = 0;
245
246         odd = 0;
247         while (len) {
248                 x = blocklen;
249                 if (len < x)
250                         x = len;
251
252                 csum = ptclbsum(addr, x);
253                 if (odd)
254                         hisum += csum;
255                 else
256                         losum += csum;
257                 odd = (odd + x) & 1;
258                 len -= x;
259
260                 bp = bp->next;
261                 if (bp == NULL)
262                         break;
263                 blocklen = BLEN(bp);
264                 addr = bp->rp;
265         }
266
267         losum += hisum >> 8;
268         losum += (hisum & 0xff) << 8;
269         while ((csum = losum >> 16) != 0)
270                 losum = csum + (losum & 0xffff);
271
272         return ~losum & 0xffff;
273 }
274
275 enum {
276         Isprefix = 16,
277 };
278
279 static uint8_t prefixvals[256] = {
280         [0x00] 0 | Isprefix,
281         [0x80] 1 | Isprefix,
282         [0xC0] 2 | Isprefix,
283         [0xE0] 3 | Isprefix,
284         [0xF0] 4 | Isprefix,
285         [0xF8] 5 | Isprefix,
286         [0xFC] 6 | Isprefix,
287         [0xFE] 7 | Isprefix,
288         [0xFF] 8 | Isprefix,
289 };
290
291 #define CLASS(p) ((*( uint8_t *)(p))>>6)
292
293 extern char *v4parseip(uint8_t * to, char *from)
294 {
295         int i;
296         char *p;
297
298         p = from;
299         for (i = 0; i < 4 && *p; i++) {
300                 to[i] = strtoul(p, &p, 0);
301                 if (*p == '.')
302                         p++;
303         }
304         switch (CLASS(to)) {
305                 case 0: /* class A - 1 uint8_t net */
306                 case 1:
307                         if (i == 3) {
308                                 to[3] = to[2];
309                                 to[2] = to[1];
310                                 to[1] = 0;
311                         } else if (i == 2) {
312                                 to[3] = to[1];
313                                 to[1] = 0;
314                         }
315                         break;
316                 case 2: /* class B - 2 uint8_t net */
317                         if (i == 3) {
318                                 to[3] = to[2];
319                                 to[2] = 0;
320                         }
321                         break;
322         }
323         return p;
324 }
325
326 int isv4(uint8_t * ip)
327 {
328         unsigned short *ips = (unsigned short *)ip;
329         return 0xffff == ips[5];
330 }
331
332 /*
333  *  the following routines are unrolled with no memset's to speed
334  *  up the usual case.  They assume IP addresses are naturally aligned.
335  */
336 void v4tov6(uint8_t * v6, uint8_t * v4)
337 {
338         uint32_t *v6p = (uint32_t *)v6;
339         uint32_t *v4p = (uint32_t *)v4;
340
341         v6p[0] = 0;
342         v6p[1] = 0;
343         v6p[2] = (unsigned int)PP_NTOHL(0xffff);
344         v6p[3] = v4p[0];
345 }
346
347 int v6tov4(uint8_t * v4, uint8_t * v6)
348 {
349
350         uint32_t *v6p = (uint32_t *)v6;
351         uint32_t *v4p = (uint32_t *)v4;
352
353         if (v6p[0] == 0 && v6p[1] == 0 &&
354             v6p[2] == (unsigned int)PP_NTOHL(0xffff)) {
355                 v4p[0] = v6p[3];
356                 return 0;
357         } else {
358                 v4p[0] = 0;
359                 return -1;
360         }
361 }
362
363 uint32_t parseip(uint8_t * to, char *from)
364 {
365         int i, elipsis = 0, v4 = 1;
366         uint32_t x;
367         char *p, *op;
368
369         memset(to, 0, IPaddrlen);
370         p = from;
371         for (i = 0; i < 16 && *p; i += 2) {
372                 op = p;
373                 x = strtoul(p, &p, 16);
374                 if (*p == '.' || (*p == 0 && i == 0)) {
375                         p = v4parseip(to + i, op);
376                         i += 4;
377                         break;
378                 } else {
379                         to[i] = x >> 8;
380                         to[i + 1] = x;
381                 }
382                 if (*p == ':') {
383                         v4 = 0;
384                         if (*++p == ':') {
385                                 elipsis = i + 2;
386                                 p++;
387                         }
388                 }
389         }
390         if (i < 16) {
391                 memmove(&to[elipsis + 16 - i], &to[elipsis], i - elipsis);
392                 memset(&to[elipsis], 0, 16 - i);
393         }
394         if (v4) {
395                 to[10] = to[11] = 0xff;
396                 return nhgetl(to + 12);
397         } else
398                 return 6;
399 }
400
401 /*
402  *  hack to allow ip v4 masks to be entered in the old
403  *  style
404  */
405 uint32_t parseipmask(uint8_t * to, char *from)
406 {
407         uint32_t x;
408         int i;
409         uint8_t *p;
410
411         if (*from == '/') {
412                 /* as a number of prefix bits */
413                 i = atoi(from + 1);
414                 if (i < 0)
415                         i = 0;
416                 if (i > 128)
417                         i = 128;
418                 memset(to, 0, IPaddrlen);
419                 for (p = to; i >= 8; i -= 8)
420                         *p++ = 0xff;
421                 if (i > 0)
422                         *p = ~((1 << (8 - i)) - 1);
423                 x = nhgetl(to + IPv4off);
424         } else {
425                 /* as a straight bit mask */
426                 x = parseip(to, from);
427                 if (memcmp(to, v4prefix, IPv4off) == 0)
428                         memset(to, 0xff, IPv4off);
429         }
430         return x;
431 }
432
433 void maskip(uint8_t * from, uint8_t * mask, uint8_t * to)
434 {
435         int i;
436
437         for (i = 0; i < IPaddrlen; i++)
438                 to[i] = from[i] & mask[i];
439 }
440
441 uint8_t classmask[4][16] = {
442         {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
443          0xff, 0x00, 0x00, 0x00}
444         ,
445         {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
446          0xff, 0x00, 0x00, 0x00}
447         ,
448         {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
449          0xff, 0xff, 0x00, 0x00}
450         ,
451
452         {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
453          0xff, 0xff, 0xff, 0x00}
454         ,
455 };
456
457 uint8_t *defmask(uint8_t * ip)
458 {
459         if (isv4(ip))
460                 return classmask[ip[IPv4off] >> 6];
461         else {
462                 if (ipcmp(ip, v6loopback) == 0)
463                         return IPallbits;
464                 else if (memcmp(ip, v6linklocal, v6llpreflen) == 0)
465                         return v6linklocalmask;
466                 else if (memcmp(ip, v6sitelocal, v6slpreflen) == 0)
467                         return v6sitelocalmask;
468                 else if (memcmp(ip, v6solicitednode, v6snpreflen) == 0)
469                         return v6solicitednodemask;
470                 else if (memcmp(ip, v6multicast, v6mcpreflen) == 0)
471                         return v6multicastmask;
472                 return IPallbits;
473         }
474 }
475
476 void ipv62smcast(uint8_t * smcast, uint8_t * a)
477 {
478         assert(IPaddrlen == 16);
479         memmove(smcast, v6solicitednode, IPaddrlen);
480         smcast[13] = a[13];
481         smcast[14] = a[14];
482         smcast[15] = a[15];
483 }
484
485 /*
486  *  parse a hex mac address
487  */
488 int parsemac(uint8_t * to, char *from, int len)
489 {
490         char nip[4];
491         char *p;
492         int i;
493
494         p = from;
495         memset(to, 0, len);
496         for (i = 0; i < len; i++) {
497                 if (p[0] == '\0' || p[1] == '\0')
498                         break;
499
500                 nip[0] = p[0];
501                 nip[1] = p[1];
502                 nip[2] = '\0';
503                 p += 2;
504
505                 to[i] = strtoul(nip, 0, 16);
506                 if (*p == ':')
507                         p++;
508         }
509         return i;
510 }
511
512 /*
513  *  hashing tcp, udp, ... connections
514  *  gcc weirdness: it gave a bogus result until ron split the %= out.
515  */
516 uint32_t iphash(uint8_t * sa, uint16_t sp, uint8_t * da, uint16_t dp)
517 {
518         uint32_t ret;
519         ret = (sa[IPaddrlen - 1] << 24) ^ (sp << 16) ^ (da[IPaddrlen - 1] << 8)
520                 ^ dp;
521         ret %= Nhash;
522         return ret;
523 }
524
525 void iphtadd(struct Ipht *ht, struct conv *c)
526 {
527         uint32_t hv;
528         struct Iphash *h;
529
530         hv = iphash(c->raddr, c->rport, c->laddr, c->lport);
531         h = kzmalloc(sizeof(*h), 0);
532         if (ipcmp(c->raddr, IPnoaddr) != 0)
533                 h->match = IPmatchexact;
534         else {
535                 if (ipcmp(c->laddr, IPnoaddr) != 0) {
536                         if (c->lport == 0)
537                                 h->match = IPmatchaddr;
538                         else
539                                 h->match = IPmatchpa;
540                 } else {
541                         if (c->lport == 0)
542                                 h->match = IPmatchany;
543                         else
544                                 h->match = IPmatchport;
545                 }
546         }
547         h->c = c;
548
549         spin_lock(&ht->lock);
550         h->next = ht->tab[hv];
551         ht->tab[hv] = h;
552         spin_unlock(&ht->lock);
553 }
554
555 void iphtrem(struct Ipht *ht, struct conv *c)
556 {
557         uint32_t hv;
558         struct Iphash **l, *h;
559
560         hv = iphash(c->raddr, c->rport, c->laddr, c->lport);
561         spin_lock(&ht->lock);
562         for (l = &ht->tab[hv]; (*l) != NULL; l = &(*l)->next)
563                 if ((*l)->c == c) {
564                         h = *l;
565                         (*l) = h->next;
566                         kfree(h);
567                         break;
568                 }
569         spin_unlock(&ht->lock);
570 }
571
572 /* look for a matching conversation with the following precedence
573  *      connected && raddr,rport,laddr,lport
574  *      announced && laddr,lport
575  *      announced && *,lport
576  *      announced && laddr,*
577  *      announced && *,*
578  */
579 struct conv *iphtlook(struct Ipht *ht, uint8_t * sa, uint16_t sp, uint8_t * da,
580                                           uint16_t dp)
581 {
582         uint32_t hv;
583         struct Iphash *h;
584         struct conv *c;
585
586         /* exact 4 pair match (connection) */
587         hv = iphash(sa, sp, da, dp);
588         spin_lock(&ht->lock);
589         for (h = ht->tab[hv]; h != NULL; h = h->next) {
590                 if (h->match != IPmatchexact)
591                         continue;
592                 c = h->c;
593                 if (sp == c->rport && dp == c->lport
594                         && ipcmp(sa, c->raddr) == 0 && ipcmp(da, c->laddr) == 0) {
595                         spin_unlock(&ht->lock);
596                         return c;
597                 }
598         }
599
600         /* match local address and port */
601         hv = iphash(IPnoaddr, 0, da, dp);
602         for (h = ht->tab[hv]; h != NULL; h = h->next) {
603                 if (h->match != IPmatchpa)
604                         continue;
605                 c = h->c;
606                 if (dp == c->lport && ipcmp(da, c->laddr) == 0) {
607                         spin_unlock(&ht->lock);
608                         return c;
609                 }
610         }
611
612         /* match just port */
613         hv = iphash(IPnoaddr, 0, IPnoaddr, dp);
614         for (h = ht->tab[hv]; h != NULL; h = h->next) {
615                 if (h->match != IPmatchport)
616                         continue;
617                 c = h->c;
618                 if (dp == c->lport) {
619                         spin_unlock(&ht->lock);
620                         return c;
621                 }
622         }
623
624         /* match local address */
625         hv = iphash(IPnoaddr, 0, da, 0);
626         for (h = ht->tab[hv]; h != NULL; h = h->next) {
627                 if (h->match != IPmatchaddr)
628                         continue;
629                 c = h->c;
630                 if (ipcmp(da, c->laddr) == 0) {
631                         spin_unlock(&ht->lock);
632                         return c;
633                 }
634         }
635
636         /* look for something that matches anything */
637         hv = iphash(IPnoaddr, 0, IPnoaddr, 0);
638         for (h = ht->tab[hv]; h != NULL; h = h->next) {
639                 if (h->match != IPmatchany)
640                         continue;
641                 c = h->c;
642                 spin_unlock(&ht->lock);
643                 return c;
644         }
645         spin_unlock(&ht->lock);
646         return NULL;
647 }