]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/rip.c
ip/cifsd: fix %.*s format xdirflush() path
[plan9front.git] / sys / src / cmd / ip / rip.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ip.h>
5
6 enum
7 {
8         Version=        1,
9         Pasize=         4,
10
11         /*
12          *  definitions that are innately tied to BSD
13          */
14         AF_INET=        2,
15         AF_UNSPEC=      0,
16
17         /*
18          *  Packet types.
19          */
20         Request=        1,
21         Response=       2,
22         Traceon=        3,
23         Traceoff=       4,
24
25         Infinity=       16,     /* infinite hop count */
26         Maxpacket=      488,    /* largest packet body */
27 };
28
29
30 /*
31  *  network info
32  */
33 typedef struct Rip      Rip;
34 struct Rip
35 {
36         uchar   family[2];
37         uchar   port[2];
38         uchar   addr[Pasize];
39         uchar   pad[8];
40         uchar   metric[4];
41 };
42 typedef struct Ripmsg   Ripmsg;
43 struct Ripmsg
44 {
45         uchar   type;
46         uchar   vers;
47         uchar   pad[2];
48         Rip     rip[1];         /* the rest of the packet consists of routes */
49 };
50
51 enum
52 {
53         Maxroutes=      (Maxpacket-4)/sizeof(Ripmsg),
54 };
55
56 /*
57  *  internal route info
58  */
59 enum
60 {
61         Nroute= 2048,           /* this has to be smaller than what /ip has */
62         Nhash=  256,            /* routing hash buckets */
63         Nifc=   16,
64 };
65
66 typedef struct Route    Route;
67 struct Route
68 {
69         Route   *next;
70
71         uchar   dest[Pasize];
72         uchar   mask[Pasize];
73         uchar   gate[Pasize];
74         int     metric;
75         int     inuse;
76         long    time;
77 };
78 struct {
79         Route   route[Nroute];
80         Route   *hash[Nhash];
81         int     nroute;
82         Route   def;    /* default route (immutable by us) */
83 } ralloc;
84
85 typedef struct Ifc      Ifc;
86 struct Ifc
87 {
88         int     bcast;
89         uchar   addr[Pasize];   /* my address */
90         uchar   mask[Pasize];   /* subnet mask */
91         uchar   net[Pasize];    /* subnet */
92         uchar   *cmask;         /* class mask */
93         uchar   cnet[Pasize];   /* class net */
94 };
95 struct {
96         Ifc     ifc[Nifc];
97         int     nifc;
98 } ialloc;
99
100 /*
101  *  specific networks to broadcast on
102  */
103 typedef struct Bnet Bnet;
104 struct Bnet
105 {
106         Bnet    *next;
107         uchar   addr[Pasize];
108 };
109 Bnet    *bnets;
110
111 int     ripfd;
112 long    now;
113 int     debug;
114 int     readonly;
115 char    routefile[256];
116 char    netdir[256];
117
118 int     openport(void);
119 void    readroutes(void);
120 void    readifcs(void);
121 void    considerroute(Route*);
122 void    installroute(Route*);
123 void    removeroute(Route*);
124 uchar   *getmask(uchar*);
125 void    broadcast(void);
126 void    timeoutroutes(void);
127
128 void
129 fatal(int syserr, char *fmt, ...)
130 {
131         char buf[ERRMAX], sysbuf[ERRMAX];
132         va_list arg;
133
134         va_start(arg, fmt);
135         vseprint(buf, buf+sizeof(buf), fmt, arg);
136         va_end(arg);
137         if(syserr) {
138                 errstr(sysbuf, sizeof sysbuf);
139                 fprint(2, "routed: %s: %s\n", buf, sysbuf);
140         }
141         else
142                 fprint(2, "routed: %s\n", buf);
143         exits(buf);
144 }
145
146 uchar*
147 v4defmask(uchar *ip)
148 {
149         uchar v6ip[IPaddrlen];
150
151         v4tov6(v6ip, ip);
152         ip = defmask(v6ip);
153         return ip+IPv4off;
154 }
155
156 void
157 v4maskip(uchar *from, uchar *mask, uchar *to)
158 {
159         int i;
160
161         for(i = 0; i < Pasize; i++)
162                 *to++ = *from++ & *mask++;
163 }
164
165 void
166 v6tov4mask(uchar *v4, uchar *v6)
167 {
168         memmove(v4, v6+IPv4off, 4);
169 }
170
171 int
172 v4parseipandmask(uchar *ip, uchar *mask, char *p, char *m)
173 {
174         uchar v6ip[IPaddrlen], v6mask[IPaddrlen];
175
176         if(parseipandmask(v6ip, v6mask, p, m) == -1)
177                 return -1;
178         v6tov4mask(mask, v6mask);
179         return v6tov4(ip, v6ip);
180 }
181
182
183 #define equivip(a, b) (memcmp((a), (b), Pasize) == 0)
184
185 void
186 ding(void *u, char *msg)
187 {
188         USED(u);
189
190         if(strstr(msg, "alarm"))
191                 noted(NCONT);
192         noted(NDFLT);
193 }
194
195 void
196 usage(void)
197 {
198         fprint(2, "usage: %s [-bnd] [-x netmtpt]\n", argv0);
199         exits("usage");
200 }
201
202 void
203 main(int argc, char *argv[])
204 {
205         int dobroadcast, i, n;
206         long diff;
207         char *p;
208         char buf[2*1024];
209         uchar raddr[Pasize];
210         Bnet *bn, **l;
211         Udphdr *up;
212         Rip *r;
213         Ripmsg *m;
214         Route route;
215         static long btime;
216
217         setnetmtpt(netdir, sizeof(netdir), nil);
218         dobroadcast = 0;
219         ARGBEGIN{
220         case 'b':
221                 dobroadcast++;
222                 break;
223         case 'd':
224                 debug++;
225                 break;
226         case 'n':
227                 readonly++;
228                 break;
229         case 'x':
230                 p = ARGF();
231                 if(p == nil)
232                         usage();
233                 setnetmtpt(netdir, sizeof(netdir), p);
234                 break;
235         default:
236                 usage();
237         }ARGEND
238
239         /* specific broadcast nets */
240         l = &bnets;
241         while(argc > 0){
242                 bn = (Bnet*)malloc(sizeof(Bnet));
243                 if(bn == 0)
244                         fatal(1, "out of mem");
245                 v4parseip(bn->addr, *argv);
246                 *l = bn;
247                 l = &bn->next;
248                 argc--;
249                 argv++;
250                 dobroadcast++;
251         }
252
253         /* command returns */
254         if(!debug)
255                 switch(rfork(RFNOTEG|RFPROC|RFFDG|RFNOWAIT)) {
256                 case -1:
257                         fatal(1, "fork");
258                 case 0:
259                         break;
260                 default:
261                         exits(0);
262                 }
263
264
265         fmtinstall('E', eipfmt);
266         fmtinstall('V', eipfmt);
267
268         snprint(routefile, sizeof(routefile), "%s/iproute", netdir);
269         snprint(buf, sizeof(buf), "%s/iproute", netdir);
270
271         now = time(0);
272         readifcs();
273         readroutes();
274
275         notify(ding);
276
277         ripfd = openport();
278         for(;;) {
279                 diff = btime - time(0);
280                 if(diff <= 0){
281                         if(dobroadcast)
282                                 broadcast();
283                         timeoutroutes();
284
285                         btime = time(0) + 2*60;
286                         diff = 2*60;
287                 }
288                 alarm(diff*1000);
289                 n = read(ripfd, buf, sizeof(buf));
290                 alarm(0);
291                 if(n <= 0)
292                         continue;
293
294                 n = (n - Udphdrsize - 4) / sizeof(Rip);
295                 if(n <= 0)
296                         continue;
297
298                 up = (Udphdr*)buf;
299                 m = (Ripmsg*)(buf+Udphdrsize);
300                 if(m->type != Response || m->vers != Version)
301                         continue;
302                 v6tov4(raddr, up->raddr);
303
304                 /* ignore our own messages */
305                 for(i = 0; i < ialloc.nifc; i++)
306                         if(equivip(ialloc.ifc[i].addr, raddr))
307                                 continue;
308
309                 now = time(0);
310                 for(r = m->rip; r < &m->rip[n]; r++){
311                         memmove(route.gate, raddr, Pasize);
312                         memmove(route.mask, getmask(r->addr), Pasize);
313                         v4maskip(r->addr, route.mask, route.dest);
314                         route.metric = nhgetl(r->metric) + 1;
315                         if(route.metric < 1)
316                                 continue;
317                         considerroute(&route);
318                 }
319         }
320         /* not reached */
321 }
322
323 int
324 openport(void)
325 {
326         int ripctl, rip;
327         char data[128], devdir[40];
328
329         snprint(data, sizeof(data), "%s/udp!*!rip", netdir);
330         ripctl = announce(data, devdir);
331         if(ripctl < 0)
332                 fatal(1, "can't announce");
333         if(fprint(ripctl, "headers") < 0)
334                 fatal(1, "can't set header mode");
335
336         sprint(data, "%s/data", devdir);
337         rip = open(data, ORDWR);
338         if(rip < 0)
339                 fatal(1, "open udp data");
340         return rip;
341 }
342
343 Ipifc *ifcs;
344
345 void
346 readifcs(void)
347 {
348         Ipifc *ifc;
349         Iplifc *lifc;
350         Ifc *ip;
351         Bnet *bn;
352         Route route;
353         int i;
354
355         ifcs = readipifc(netdir, ifcs, -1);
356         i = 0;
357         for(ifc = ifcs; ifc != nil; ifc = ifc->next){
358                 for(lifc = ifc->lifc; lifc != nil && i < Nifc; lifc = lifc->next){
359                         if(!isv4(lifc->ip))
360                                 continue;
361                         ip = &ialloc.ifc[i++];
362                         v6tov4(ip->addr, lifc->ip);
363                         v6tov4mask(ip->mask, lifc->mask);
364                         v6tov4(ip->net, lifc->net);
365                         ip->cmask = v4defmask(ip->net);
366                         v4maskip(ip->net, ip->cmask, ip->cnet);
367                         ip->bcast = 0;
368
369                         /* add as a route */
370                         memmove(route.mask, ip->mask, Pasize);
371                         memmove(route.dest, ip->net, Pasize);
372                         memset(route.gate, 0, Pasize);
373                         route.metric = 0;
374                         considerroute(&route);
375
376                         /* mark as broadcast */
377                         if(bnets == 0)
378                                 ip->bcast = 1;
379                         else for(bn = bnets; bn; bn = bn->next)
380                                 if(memcmp(bn->addr, ip->net, Pasize) == 0){
381                                         ip->bcast = 1;
382                                         break;
383                                 }
384                 }
385         }
386         ialloc.nifc = i;
387 }
388
389 void
390 readroutes(void)
391 {
392         int n;
393         char *p;
394         Biobuf *b;
395         char *f[6];
396         Route route;
397
398         b = Bopen(routefile, OREAD);
399         if(b == 0)
400                 return;
401         while(p = Brdline(b, '\n')){
402                 p[Blinelen(b)-1] = 0;
403                 n = getfields(p, f, 6, 1, " \t");
404                 if(n < 5)
405                         continue;
406                 if(v4parseipandmask(route.dest, route.mask, f[0], f[1]) == -1)
407                         continue;
408                 v4parseip(route.gate, f[2]);
409                 route.metric = Infinity;
410                 if(equivip(route.dest, ralloc.def.dest) && equivip(route.mask, ralloc.def.mask))
411                         memmove(ralloc.def.gate, route.gate, Pasize);
412                 else if(!equivip(route.dest, route.gate) && strchr(f[3], 'i') == 0)
413                         considerroute(&route);
414         }
415         Bterm(b);
416 }
417
418 /*
419  *  route's hashed by net, not subnet
420  */
421 ulong
422 rhash(uchar *d)
423 {
424         ulong h;
425         uchar net[Pasize];
426
427         v4maskip(d, v4defmask(d), net);
428         h = net[0] + net[1] + net[2];
429         return h % Nhash;
430 }
431
432 /*
433  *  consider installing a route.  Do so only if it is better than what
434  *  we have.
435  */
436 void
437 considerroute(Route *r)
438 {
439         ulong h;
440         Route *hp;
441
442         if(debug)
443                 fprint(2, "consider %16V & %16V -> %16V %d\n", r->dest, r->mask, r->gate, r->metric);
444
445         r->next = 0;
446         r->time = now;
447         r->inuse = 1;
448
449         /* don't allow our default route to be highjacked */
450         if(equivip(r->dest, ralloc.def.dest) || equivip(r->mask, ralloc.def.mask))
451                 return;
452
453         h = rhash(r->dest);
454         for(hp = ralloc.hash[h]; hp; hp = hp->next){
455                 if(equivip(hp->dest, r->dest)){
456                         /*
457                          *  found a match, replace if better (or much newer)
458                          */
459                         if(r->metric < hp->metric || now-hp->time > 5*60){
460                                 removeroute(hp);
461                                 memmove(hp->mask, r->mask, Pasize);
462                                 memmove(hp->gate, r->gate, Pasize);
463                                 hp->metric = r->metric;
464                                 installroute(hp);
465                         }
466                         if(equivip(hp->gate, r->gate))
467                                 hp->time = now;
468                         return;
469                 }
470         }
471
472         /*
473          *  no match, look for space
474          */
475         for(hp = ralloc.route; hp < &ralloc.route[Nroute]; hp++)
476                 if(hp->inuse == 0)
477                         break;
478
479         if(hp == 0)
480                 fatal(0, "no more routes");
481
482         memmove(hp, r, sizeof(Route));
483         hp->next = ralloc.hash[h];
484         ralloc.hash[h] = hp;
485         installroute(hp);
486 }
487
488 void
489 removeroute(Route *r)
490 {
491         int fd;
492
493         fd = open(routefile, ORDWR);
494         if(fd < 0){
495                 fprint(2, "can't open oproute\n");
496                 return;
497         }
498         if(!readonly)
499                 fprint(fd, "delete %V", r->dest);
500         if(debug)
501                 fprint(2, "removeroute %V\n", r->dest);
502         close(fd);
503 }
504
505 /*
506  *  pass a route to the kernel or /ip.  Don't bother if it is just the default
507  *  gateway.
508  */
509 void
510 installroute(Route *r)
511 {
512         int fd;
513         ulong h;
514         Route *hp;
515         uchar net[Pasize];
516
517         /*
518          *  don't install routes whose gateway is 00000000
519          */
520         if(equivip(r->gate, ralloc.def.dest))
521                 return;
522
523         fd = open(routefile, ORDWR);
524         if(fd < 0){
525                 fprint(2, "can't open oproute\n");
526                 return;
527         }
528         h = rhash(r->dest);
529
530         /*
531          *  if the gateway is the same as the default gateway
532          *  we may be able to avoid a entry in the kernel
533          */
534         if(equivip(r->gate, ralloc.def.gate)){
535                 /*
536                  *  look for a less specific match
537                  */
538                 for(hp = ralloc.hash[h]; hp; hp = hp->next){
539                         v4maskip(hp->mask, r->dest, net);
540                         if(equivip(net, hp->dest) && !equivip(hp->gate, ralloc.def.gate))
541                                 break;
542                 }
543                 /*
544                  *  if no less specific match, just use the default
545                  */
546                 if(hp == 0){
547                         if(!readonly)
548                                 fprint(fd, "delete %V", r->dest);
549                         if(debug)
550                                 fprint(2, "delete %V\n", r->dest);
551                         close(fd);
552                         return;
553                 }
554         }
555         if(!readonly)
556                 fprint(fd, "add %V %V %V", r->dest, r->mask, r->gate);
557         if(debug)
558                 fprint(2, "add %V & %V -> %V\n", r->dest, r->mask, r->gate);
559         close(fd);
560 }
561
562 /*
563  *  return true of dest is on net
564  */
565 int
566 onnet(uchar *dest, uchar *net, uchar *netmask)
567 {
568         uchar dnet[Pasize];
569
570         v4maskip(dest, netmask, dnet);
571         return equivip(dnet, net);
572 }
573
574 /*
575  *  figure out what mask to use, if we have a direct connected network
576  *  with the same class net use its subnet mask.
577  */
578 uchar*
579 getmask(uchar *dest)
580 {
581         int i;
582         Ifc *ip;
583         ulong mask, nmask;
584         uchar *m;
585
586         m = 0;
587         mask = 0xffffffff;
588         for(i = 0; i < ialloc.nifc; i++){
589                 ip = &ialloc.ifc[i];
590                 if(onnet(dest, ip->cnet, ip->cmask)){
591                         nmask = nhgetl(ip->mask);
592                         if(nmask < mask){
593                                 mask = nmask;
594                                 m = ip->mask;
595                         }
596                 }
597         }
598
599         if(m == 0)
600                 m = v4defmask(dest);
601         return m;
602 }
603
604 /*
605  *  broadcast routes onto all networks
606  */
607 void
608 sendto(Ifc *ip)
609 {
610         int h, n;
611         uchar raddr[Pasize], mbuf[Udphdrsize+512];
612         Ripmsg *m;
613         Route *r;
614         Udphdr *u;
615
616         u = (Udphdr*)mbuf;
617         for(n = 0; n < Pasize; n++)
618                 raddr[n] = ip->net[n] | ~(ip->mask[n]);
619         v4tov6(u->raddr, raddr);
620         hnputs(u->rport, 520);
621         m = (Ripmsg*)(mbuf+Udphdrsize);
622         m->type = Response;
623         m->vers = Version;
624         if(debug)
625                 fprint(2, "to %V\n", u->raddr);
626
627         n = 0;
628         for(h = 0; h < Nhash; h++){
629                 for(r = ralloc.hash[h]; r; r = r->next){
630                         /*
631                          *  don't send any route back to the net
632                          *  it came from
633                          */
634                         if(onnet(r->gate, ip->net, ip->mask))
635                                 continue;
636
637                         /*
638                          *  don't tell a network about itself
639                          */
640                         if(equivip(r->dest, ip->net))
641                                 continue;
642
643                         /*
644                          *  don't tell nets about other net's subnets
645                          */
646                         if(!equivip(r->mask, v4defmask(r->dest))
647                         && !equivip(ip->cmask, v4defmask(r->dest)))
648                                 continue;
649
650                         memset(&m->rip[n], 0, sizeof(m->rip[n]));
651                         memmove(m->rip[n].addr, r->dest, Pasize);
652                         if(r->metric < 1)
653                                 hnputl(m->rip[n].metric, 1);
654                         else
655                                 hnputl(m->rip[n].metric, r->metric);
656                         hnputs(m->rip[n].family, AF_INET);
657
658                         if(debug)
659                                 fprint(2, " %16V & %16V -> %16V %2d\n",
660                                         r->dest, r->mask, r->gate, r->metric);
661
662                         if(++n == Maxroutes && !readonly){
663                                 write(ripfd, mbuf, Udphdrsize + 4 + n*20);
664                                 n = 0;
665                         }
666                 }
667         }
668
669         if(n && !readonly)
670                 write(ripfd, mbuf, Udphdrsize+4+n*20);
671 }
672 void
673 broadcast(void)
674 {
675         int i;
676
677         readifcs();
678         for(i = 0; i < ialloc.nifc; i++){
679                 if(ialloc.ifc[i].bcast)
680                         sendto(&ialloc.ifc[i]);
681         }
682 }
683
684 /*
685  *  timeout any routes that haven't been refreshed and aren't wired
686  */
687 void
688 timeoutroutes(void)
689 {
690         int h;
691         long now;
692         Route *r, **l;
693
694         now = time(0);
695
696         for(h = 0; h < Nhash; h++){
697                 l = &ralloc.hash[h];
698                 for(r = *l; r; r = *l){
699                         if(r->metric < Infinity && now - r->time > 10*60){
700                                 removeroute(r);
701                                 r->inuse = 0;
702                                 *l = r->next;
703                                 continue;
704                         }
705                         l = &r->next;
706                 }
707         }
708 }