1implement Rip; 2 3# basic RIP implementation 4# understands v2, sends v1 5 6include "sys.m"; 7 sys: Sys; 8 9include "draw.m"; 10 11include "bufio.m"; 12 bufio: Bufio; 13 Iobuf: import bufio; 14 15include "daytime.m"; 16 daytime: Daytime; 17 18include "dial.m"; 19 dial: Dial; 20 21 22include "ip.m"; 23 ip: IP; 24 IPaddr, Ifcaddr, Udphdr: import ip; 25 26include "attrdb.m"; 27 attrdb: Attrdb; 28 29include "arg.m"; 30 31Rip: module 32{ 33 init: fn(nil: ref Draw->Context, nil: list of string); 34}; 35 36# rip header: 37# op[1] version[1] pad[2] 38 39Oop: con 0; # op: byte 40Oversion: con 1; # version: byte 41Opad: con 2; # 2 byte pad 42Riphdrlen: con Opad+2; # op[1] version[1] mbz[2] 43 44# rip route entry: 45# type[2] tag[2] addr[4] mask[4] nexthop[4] metric[4] 46 47Otype: con 0; # type[2] 48Otag: con Otype+2; # tag[2] v2 or mbz v1 49Oaddr: con Otag+2; # addr[4] 50Omask: con Oaddr+4; # mask[4] v2 or mbz v1 51Onexthop: con Omask+4; 52Ometric: con Onexthop+4; # metric[4] 53Ipdestlen: con Ometric+4; 54 55Maxripmsg: con 512; 56 57# operations 58OpRequest: con 1; # want route 59OpReply: con 2; # all or part of route table 60 61HopLimit: con 16; # defined by protocol as `infinity' 62RoutesInPkt: con 25; # limit defined by protocol 63RIPport: con 520; 64 65Expired: con 180; 66Discard: con 240; 67 68OutputRate: con 60; # seconds between routing table transmissions 69 70NetworkCost: con 1; # assume the simple case 71 72Gateway: adt { 73 dest: IPaddr; 74 mask: IPaddr; 75 gateway: IPaddr; 76 metric: int; 77 valid: int; 78 changed: int; 79 local: int; 80 time: int; 81 82 contains: fn(g: self ref Gateway, a: IPaddr): int; 83}; 84 85netfd: ref Sys->FD; 86routefd: ref Sys->FD; 87AF_INET: con 2; 88 89routes: array of ref Gateway; 90Routeinc: con 50; 91defroute: ref Gateway; 92debug := 0; 93nochange := 0; 94quiet := 1; 95myversion := 1; # default protocol version 96logfile := "iproute"; 97netdir := "/net"; 98now: int; 99nets: list of ref Ifcaddr; 100addrs: list of IPaddr; 101 102syslog(nil: int, nil: string, s: string) 103{ 104 sys->print("rip: %s\n", s); 105} 106 107init(nil: ref Draw->Context, args: list of string) 108{ 109 sys = load Sys Sys->PATH; 110 bufio = load Bufio Bufio->PATH; 111 daytime = load Daytime Daytime->PATH; 112 dial = load Dial Dial->PATH; 113 ip = load IP IP->PATH; 114 ip->init(); 115 116 arg := load Arg Arg->PATH; 117 arg->init(args); 118 arg->setusage("ip/rip [-d] [-r]"); 119 while((o := arg->opt()) != 0) 120 case o { 121 'd' => debug++; 122 'b' => quiet = 0; 123 '2' => myversion = 2; 124 'n' => nochange = 1; 125 'x' => netdir = arg->earg(); 126 * => arg->usage(); 127 } 128 args = arg->argv(); 129 if(args != nil) 130 quiet = 0; 131 for(; args != nil; args = tl args){ 132 (ok, a) := IPaddr.parse(hd args); 133 if(ok < 0) 134 fatal(sys->sprint("invalid address: %s", hd args)); 135 addrs = a :: addrs; 136 } 137 arg = nil; 138 139 sys->pctl(Sys->NEWPGRP|Sys->FORKFD|Sys->FORKNS, nil); 140 141 whereami(); 142 addlocal(); 143 144 routefd = sys->open(sys->sprint("%s/iproute", netdir), Sys->ORDWR); 145 if(routefd == nil) 146 fatal(sys->sprint("can't open %s/iproute: %r", netdir)); 147 readroutes(); 148 149 syslog(0, logfile, "started"); 150 151 netfd = riplisten(); 152 153 # broadcast request for all routes 154 155 if(!quiet){ 156 sendall(OpRequest, 0); 157 spawn sender(); 158 } 159 160 # read routing requests 161 162 buf := array[8192] of byte; 163 while((nb := sys->read(netfd, buf, len buf)) > 0){ 164 nb -= Riphdrlen + IP->Udphdrlen; 165 if(nb < 0) 166 continue; 167 uh := Udphdr.unpack(buf, IP->Udphdrlen); 168 hdr := buf[IP->Udphdrlen:]; 169 version := int hdr[Oversion]; 170 if(version < 1) 171 continue; 172 bp := buf[IP->Udphdrlen + Riphdrlen:]; 173 case int hdr[Oop] { 174 OpRequest => 175 # TO DO: transmit in response to request? only if something interesting to say... 176 ; 177 178 OpReply => 179 # wrong source port? 180 if(uh.rport != RIPport) 181 continue; 182 # my own broadcast? 183 if(ismyaddr(uh.raddr)) 184 continue; 185 now = daytime->now(); 186 if(debug > 1) 187 sys->fprint(sys->fildes(2), "from %s:\n", uh.raddr.text()); 188 for(; (nb -= Ipdestlen) >= 0; bp = bp[Ipdestlen:]) 189 unpackroute(bp, version, uh.raddr); 190 * => 191 if(debug) 192 sys->print("rip: unexpected op: %d\n", int hdr[Oop]); 193 } 194 } 195} 196 197whereami() 198{ 199 for(ifcs := ip->readipifc(netdir, -1).t0; ifcs != nil; ifcs = tl ifcs) 200 for(al := (hd ifcs).addrs; al != nil; al = tl al){ 201 ifa := hd al; 202 if(!ifa.ip.isv4()) 203 continue; 204 # how to tell broadcast? must be told? actually, it's in /net/iproute 205 nets = ifa :: nets; 206 } 207} 208 209ismyaddr(a: IPaddr): int 210{ 211 for(l := nets; l != nil; l = tl l) 212 if((hd l).ip.eq(a)) 213 return 1; 214 return 0; 215} 216 217addlocal() 218{ 219 for(l := nets; l != nil; l = tl l){ 220 ifc := hd l; 221 g := lookup(ifc.net); 222 g.valid = 1; 223 g.local = 1; 224 g.gateway = ifc.ip; 225 g.mask = ifc.mask; 226 g.metric = NetworkCost; 227 g.time = 0; 228 g.changed = 1; 229 if(debug) 230 syslog(0, logfile, sys->sprint("Existing: %s & %s -> %s", g.dest.text(), g.mask.masktext(), g.gateway.text())); 231 } 232} 233 234# 235# record any existing routes 236# 237readroutes() 238{ 239 now = daytime->now(); 240 b := bufio->fopen(routefd, Sys->OREAD); 241 while((l := b.gets('\n')) != nil){ 242 (nf, flds) := sys->tokenize(l, " \t"); 243 if(nf >= 5){ 244 flags := hd tl tl tl flds; 245 if(flags == nil || flags[0] != '4' || contains(flags, "ibum")) 246 continue; 247 g := lookup(parseip(hd flds)); 248 g.mask = parsemask(hd tl flds); 249 g.gateway = parseip(hd tl tl flds); 250 g.metric = HopLimit; 251 g.time = now; 252 g.changed = 1; 253 if(debug) 254 syslog(0, logfile, sys->sprint("Existing: %s & %s -> %s", g.dest.text(), g.mask.masktext(), g.gateway.text())); 255 if(iszero(g.dest) && iszero(g.mask)){ 256 defroute = g; 257 g.local = 1; 258 }else if(defroute != nil && g.dest.eq(defroute.gateway)) 259 continue; 260 else 261 g.local = !ismyaddr(g.gateway); 262 } 263 } 264} 265 266unpackroute(b: array of byte, version: int, gwa: IPaddr) 267{ 268 # check that it's an IP route, valid metric, MBZ fields zero 269 270 if(b[0] != byte 0 || b[1] != byte AF_INET){ 271 if(debug > 1) 272 sys->fprint(sys->fildes(2), "\t-- unknown address type %x,%x\n", int b[0], int b[1]); 273 return; 274 } 275 dest := IPaddr.newv4(b[Oaddr:]); 276 mask: IPaddr; 277 if(version == 1){ 278 # check MBZ fields 279 if(ip->get2(b, 2) | ip->get4(b, Omask) | ip->get4(b, Onexthop)){ 280 if(debug > 1) 281 sys->fprint(sys->fildes(2), "\t-- non-zero MBZ\n"); 282 return; 283 } 284 mask = maskgen(dest); 285 }else if(version == 2){ 286 if(ip->get4(b, Omask)) 287 mask = IPaddr.newv4(b[Omask:]); 288 else 289 mask = maskgen(dest); 290 if(ip->get4(b, Onexthop)) 291 gwa = IPaddr.newv4(b[Onexthop:]); 292 } 293 metric := ip->get4(b, Ometric); 294 if(debug > 1) 295 sys->fprint(sys->fildes(2), "\t%s %d\n", dest.text(), metric); 296 if(metric <= 0 || metric > HopLimit) 297 return; 298 299 # 1058/3.4.2: response processing 300 # ignore route if IP address is: 301 # class D or E 302 # net 0 (except perhaps 0.0.0.0) 303 # net 127 304 # broadcast address (all 1s host part) 305 # we allow host routes 306 307 if(dest.ismulticast() || dest.a[0] == byte 0 || dest.a[0] == byte 16r7F){ 308 if(debug > 1) 309 sys->fprint(sys->fildes(2), "\t%s %d invalid addr\n", dest.text(), metric); 310 return; 311 } 312 if(isbroadcast(dest, mask)){ 313 if(debug > 1) 314 sys->fprint(sys->fildes(2), "\t%s & %s -> broadcast\n", dest.text(), mask.masktext()); 315 return; 316 } 317 318 # update the metric min(metric+NetworkCost, HopLimit) 319 320 metric += NetworkCost; 321 if(metric > HopLimit) 322 metric = HopLimit; 323 324 updateroute(dest, mask, gwa, metric); 325} 326 327updateroute(dest, mask, gwa: IPaddr, metric: int) 328{ 329 # RFC1058 rules page 27-28, with optional replacement of expiring routes 330 r := lookup(dest); 331 if(r.valid){ 332 if(r.local) 333 return; # local, don't touch 334 if(r.gateway.eq(gwa)){ 335 if(metric != HopLimit){ 336 r.metric = metric; 337 r.time = now; 338 }else{ 339 # metric == HopLimit 340 if(r.metric != HopLimit){ 341 r.metric = metric; 342 r.changed = 1; 343 r.time = now - (Discard-120); 344 delroute(r); # don't use it for routing 345 # route remains valid but advertised with metric HopLimit 346 } else if(now >= r.time+Discard){ 347 delroute(r); # finally dead 348 r.valid = 0; 349 r.changed = 1; 350 } 351 } 352 }else if(metric < r.metric || 353 metric != HopLimit && metric == r.metric && now > r.time+Expired/2){ 354 delroute(r); 355 r.metric = metric; 356 r.gateway = gwa; 357 r.time = now; 358 addroute(r); 359 } 360 } else if(metric < HopLimit){ # new entry 361 362 # 1058/3.4.2: don't add route-to-host if host is on net/subnet 363 # for which we have at least as good a route 364 365 if(!mask.eq(ip->allbits) || 366 ((pr := findroute(dest)) == nil || metric <= pr.metric)){ 367 r.valid = 1; 368 r.changed = 1; 369 r.time = now; 370 r.metric = metric; 371 r.dest = dest; 372 r.mask = mask; 373 r.gateway = gwa; 374 addroute(r); 375 } 376 } 377} 378 379sender() 380{ 381 for(;;){ 382 sys->sleep(OutputRate*1000); # could add some random fizz 383 sendall(OpReply, 1); 384 } 385} 386 387onlist(a: IPaddr, l: list of IPaddr): int 388{ 389 for(; l != nil; l = tl l) 390 if(a.eq(hd l)) 391 return 1; 392 return 0; 393} 394 395sendall(op: int, changes: int) 396{ 397 for(l := nets; l != nil; l = tl l){ 398 if(addrs != nil && !onlist((hd l).net, addrs)) 399 continue; 400 a := (hd l).net.copy(); 401 b := (ip->allbits).maskn((hd l).mask); 402 for(i := 0; i < len a.a; i++) 403 a.a[i] |= b.a[i]; 404 sendroutes(hd l, a, op, changes); 405 } 406 for(i := 0; i < len routes; i++) 407 if((r := routes[i]) != nil) 408 r.changed = 0; 409} 410 411zeroentry := array[Ipdestlen] of {* => byte 0}; 412 413sendroutes(ifc: ref Ifcaddr, dst: IPaddr, op: int, changes: int) 414{ 415 if(debug > 1) 416 sys->print("rip: send %s\n", dst.text()); 417 buf := array[Maxripmsg+IP->Udphdrlen] of byte; 418 hdr := Udphdr.new(); 419 hdr.lport = hdr.rport = RIPport; 420 hdr.raddr = dst; # needn't copy 421 hdr.pack(buf, IP->Udphdrlen); 422 o := IP->Udphdrlen; 423 buf[o] = byte op; 424 buf[o+1] = byte myversion; 425 buf[o+2] = byte 0; 426 buf[o+3] = byte 0; 427 o += Riphdrlen; 428# rips := buf[IP->Udphdrlen+Riphdrlen:]; 429 if(op == OpRequest){ 430 buf[o:] = zeroentry; 431 ip->put4(buf, o+Ometric, HopLimit); 432 o += Ipdestlen; 433 } else { 434 # send routes 435 for(i:=0; i<len routes; i++){ 436 r := routes[i]; 437 if(r == nil || !r.valid || changes && !r.changed) 438 continue; 439 if(r == defroute) 440 continue; 441 if(r.dest.eq(ifc.net) || isonnet(r.dest, ifc)) 442 continue; 443 netmask := r.dest.classmask(); 444 subnet := !r.mask.eq(netmask); 445 if(myversion < 2 && !r.mask.eq(ip->allbits)){ 446 # if not a host route, don't let a subnet route leave its net 447 if(subnet && !netmask.eq(ifc.ip.classmask())) 448 continue; 449 } 450 if(o+Ipdestlen > IP->Udphdrlen+Maxripmsg){ 451 if(sys->write(netfd, buf, o) < 0) 452 sys->fprint(sys->fildes(2), "RIP write failed: %r\n"); 453 o = IP->Udphdrlen + Riphdrlen; 454 } 455 buf[o:] = zeroentry; 456 ip->put2(buf, o+Otype, AF_INET); 457 buf[o+Oaddr:] = r.dest.v4(); 458 ip->put4(buf, o+Ometric, r.metric); 459 if(myversion == 2 && subnet) 460 buf[o+Omask:] = r.mask.v4(); 461 o += Ipdestlen; 462 } 463 } 464 if(o > IP->Udphdrlen+Riphdrlen && sys->write(netfd, buf, o) < 0) 465 sys->fprint(sys->fildes(2), "rip: network write to %s failed: %r\n", dst.text()); 466} 467 468lookup(addr: IPaddr): ref Gateway 469{ 470 avail := -1; 471 for(i:=0; i<len routes; i++){ 472 g := routes[i]; 473 if(g == nil || !g.valid){ 474 if(avail < 0) 475 avail = i; 476 continue; 477 } 478 if(g.dest.eq(addr)) 479 return g; 480 } 481 if(avail < 0){ 482 avail = len routes; 483 a := array[len routes+Routeinc] of ref Gateway; 484 a[0:] = routes; 485 routes = a; 486 } 487 if((g := routes[avail]) == nil){ 488 g = ref Gateway; 489 routes[avail] = g; 490 g.valid = 0; 491 } 492 g.dest = addr; 493 return g; 494} 495 496findroute(a: IPaddr): ref Gateway 497{ 498 pr: ref Gateway; 499 for(i:=0; i<len routes; i++){ 500 r := routes[i]; 501 if(r == nil || !r.valid) 502 continue; 503 if(r.contains(a) && (pr == nil || !maskle(r.mask, pr.mask))) 504 pr = r; # more specific mask 505 } 506 return pr; 507} 508 509maskgen(addr: IPaddr): IPaddr 510{ 511 net: ref Ifcaddr; 512 for(l := nets; l != nil; l = tl l){ 513 ifc := hd l; 514 if(isonnet(addr, ifc) && 515 (net == nil || maskle(ifc.mask, net.mask))) # less specific mask? 516 net = ifc; 517 } 518 if(net != nil) 519 return net.mask; 520 return addr.classmask(); 521} 522 523isonnet(a: IPaddr, n: ref Ifcaddr): int 524{ 525 return a.mask(n.mask).eq(n.net); 526} 527 528isbroadcast(a: IPaddr, mask: IPaddr): int 529{ 530 h := a.maskn(mask); # host part 531 hm := (ip->allbits).maskn(mask); # host part of mask 532 return h.eq(hm); 533} 534 535iszero(a: IPaddr): int 536{ 537 return a.eq(ip->v4noaddr) || a.eq(ip->noaddr); 538} 539 540maskle(a, b: IPaddr): int 541{ 542 return a.mask(b).eq(a); 543} 544 545# 546# add ipdest mask gateway 547# add 0.0.0.0 0.0.0.0 gateway (default) 548# delete ipdest mask 549# 550addroute(g: ref Gateway) 551{ 552 if(iszero(g.mask) && iszero(g.dest)) 553 g.valid = 0; # don't change default route 554 else if(defroute != nil && defroute.gateway.eq(g.gateway)){ 555 if(debug) 556 syslog(0, logfile, sys->sprint("default %s %s", g.dest.text(), g.mask.text())); # don't need a new entry 557 g.valid = 1; 558 g.changed = 1; 559 } else { 560 if(debug) 561 syslog(0, logfile, sys->sprint("add %s %s %s", g.dest.text(), g.mask.text(), g.gateway.text())); 562 if(nochange || sys->fprint(routefd, "add %s %s %s", g.dest.text(), g.mask.text(), g.gateway.text()) > 0){ 563 g.valid = 1; 564 g.changed = 1; 565 } 566 } 567} 568 569delroute(g: ref Gateway) 570{ 571 if(debug) 572 syslog(0, logfile, sys->sprint("delete %s %s", g.dest.text(), g.mask.text())); 573 if(!nochange) 574 sys->fprint(routefd, "delete %s %s", g.dest.text(), g.mask.text()); 575} 576 577parseip(s: string): IPaddr 578{ 579 (ok, a) := IPaddr.parse(s); 580 if(ok < 0) 581 raise "bad route"; 582 return a; 583} 584 585parsemask(s: string): IPaddr 586{ 587 (ok, a) := IPaddr.parsemask(s); 588 if(ok < 0) 589 raise "bad route"; 590 return a; 591} 592 593contains(s: string, t: string): int 594{ 595 for(i := 0; i < len s; i++) 596 for(j := 0; j < len t; j++) 597 if(s[i] == t[j]) 598 return 1; 599 return 0; 600} 601 602Gateway.contains(g: self ref Gateway, a: IPaddr): int 603{ 604 return g.dest.eq(a.mask(g.mask)); 605} 606 607riplisten(): ref Sys->FD 608{ 609 addr := sys->sprint("%s/udp!*!rip", netdir); 610 c := dial->announce(addr); 611 if(c == nil) 612 fatal(sys->sprint("can't announce %s: %r", addr)); 613 if(sys->fprint(c.cfd, "headers") < 0) 614 fatal(sys->sprint("can't set udp headers: %r")); 615 fd := sys->open(c.dir+"/data", Sys->ORDWR); 616 if(fd == nil) 617 fatal(sys->sprint("can't open %s: %r", c.dir+"/data")); 618 return fd; 619} 620 621fatal(s: string) 622{ 623 syslog(0, logfile, s); 624 raise "fail:error"; 625} 626