forsyth20121205-174063a80cc13 years agoHistory
625 lines · 13.4 KB · Brainfuck
RawDownload
implement Rip; # basic RIP implementation#	understands v2, sends v1 include "sys.m";	sys: Sys; include "draw.m"; include "bufio.m";	bufio: Bufio;	Iobuf: import bufio; include "daytime.m";	daytime: Daytime; include "dial.m";	dial: Dial;  include "ip.m";	ip: IP;	IPaddr, Ifcaddr, Udphdr: import ip; include "attrdb.m";	attrdb: Attrdb; include "arg.m"; Rip: module{	init:	fn(nil: ref Draw->Context, nil: list of string);}; # rip header:#	op[1] version[1] pad[2] Oop: con 0;	# op: byteOversion: con 1;	# version: byteOpad: con 2;	# 2 byte padRiphdrlen: con	Opad+2;	# op[1] version[1] mbz[2] # rip route entry:#	type[2] tag[2] addr[4] mask[4] nexthop[4] metric[4] Otype: con 0;	# type[2]Otag: con Otype+2;	# tag[2] v2 or mbz v1Oaddr: con Otag+2;	# addr[4]Omask: con Oaddr+4;	# mask[4] v2 or mbz v1Onexthop: con Omask+4;Ometric: con Onexthop+4;	# metric[4]Ipdestlen: con Ometric+4; Maxripmsg: con 512; # operationsOpRequest: con 1;		# want routeOpReply: con 2;		# all or part of route table HopLimit: con 16;		# defined by protocol as `infinity'RoutesInPkt: con 25; 	# limit defined by protocolRIPport: con 520; Expired: con 180;Discard: con 240; OutputRate: con 60;	# seconds between routing table transmissions NetworkCost: con 1;	# assume the simple case Gateway: adt {	dest:	IPaddr;	mask:	IPaddr;	gateway:	IPaddr;	metric:	int;	valid:	int;	changed:	int;	local:	int;	time:	int; 	contains:	fn(g: self ref Gateway, a: IPaddr): int;}; netfd:	ref Sys->FD;routefd:	ref Sys->FD;AF_INET:	con 2; routes: array of ref Gateway;Routeinc: con 50;defroute: ref Gateway;debug := 0;nochange := 0;quiet := 1;myversion := 1;	# default protocol versionlogfile := "iproute";netdir := "/net";now: int;nets: list of ref Ifcaddr;addrs: list of IPaddr; syslog(nil: int, nil: string, s: string){	sys->print("rip: %s\n", s);} init(nil: ref Draw->Context, args: list of string){	sys = load Sys Sys->PATH;	bufio = load Bufio Bufio->PATH;	daytime = load Daytime Daytime->PATH;	dial = load Dial Dial->PATH;	ip = load IP IP->PATH;	ip->init(); 	arg := load Arg Arg->PATH;	arg->init(args);	arg->setusage("ip/rip [-d] [-r]");	while((o := arg->opt()) != 0)		case o {		'd' =>	debug++;		'b' =>	quiet = 0;		'2' =>	myversion = 2;		'n' =>	nochange = 1;		'x' =>	netdir = arg->earg();		* =>	arg->usage();		}	args = arg->argv();	if(args != nil)		quiet = 0;	for(; args != nil; args = tl args){		(ok, a) := IPaddr.parse(hd args);		if(ok < 0)			fatal(sys->sprint("invalid address: %s", hd args));		addrs = a :: addrs;	}	arg = nil; 	sys->pctl(Sys->NEWPGRP|Sys->FORKFD|Sys->FORKNS, nil); 	whereami();	addlocal(); 	routefd = sys->open(sys->sprint("%s/iproute", netdir), Sys->ORDWR);	if(routefd == nil)		fatal(sys->sprint("can't open %s/iproute: %r", netdir));	readroutes(); 	syslog(0, logfile, "started"); 	netfd = riplisten(); 	# broadcast request for all routes 	if(!quiet){		sendall(OpRequest, 0);		spawn sender();	} 	# read routing requests 	buf := array[8192] of byte;	while((nb := sys->read(netfd, buf, len buf)) > 0){		nb -= Riphdrlen + IP->Udphdrlen;		if(nb < 0)			continue;		uh := Udphdr.unpack(buf, IP->Udphdrlen);		hdr := buf[IP->Udphdrlen:];		version := int hdr[Oversion];		if(version < 1)			continue;		bp := buf[IP->Udphdrlen + Riphdrlen:];		case int hdr[Oop] {		OpRequest =>			# TO DO: transmit in response to request?  only if something interesting to say...			; 		OpReply =>			# wrong source port?			if(uh.rport != RIPport)				continue;			# my own broadcast?			if(ismyaddr(uh.raddr))				continue;			now = daytime->now();			if(debug > 1)				sys->fprint(sys->fildes(2), "from %s:\n", uh.raddr.text());			for(; (nb -= Ipdestlen) >= 0; bp = bp[Ipdestlen:])				unpackroute(bp, version, uh.raddr);		* =>			if(debug)				sys->print("rip: unexpected op: %d\n", int hdr[Oop]);		}	}} whereami(){	for(ifcs := ip->readipifc(netdir, -1).t0; ifcs != nil; ifcs = tl ifcs)		for(al := (hd ifcs).addrs; al != nil; al = tl al){			ifa := hd al;			if(!ifa.ip.isv4())				continue;			# how to tell broadcast? must be told? actually, it's in /net/iproute			nets = ifa :: nets;		}} ismyaddr(a: IPaddr): int{	for(l := nets; l != nil; l = tl l)		if((hd l).ip.eq(a))			return 1;	return 0;} addlocal(){	for(l := nets; l != nil; l = tl l){		ifc := hd l;		g := lookup(ifc.net);		g.valid = 1;		g.local = 1;		g.gateway = ifc.ip;		g.mask = ifc.mask;		g.metric = NetworkCost;		g.time = 0;		g.changed = 1;		if(debug)			syslog(0, logfile, sys->sprint("Existing: %s & %s -> %s", g.dest.text(), g.mask.masktext(), g.gateway.text()));	}} ## record any existing routes#readroutes(){	now = daytime->now();	b := bufio->fopen(routefd, Sys->OREAD);	while((l := b.gets('\n')) != nil){		(nf, flds) := sys->tokenize(l, " \t");		if(nf >= 5){			flags := hd tl tl tl flds;			if(flags == nil || flags[0] != '4' || contains(flags, "ibum"))				continue;			g := lookup(parseip(hd flds));			g.mask = parsemask(hd tl flds);			g.gateway = parseip(hd tl tl flds);			g.metric = HopLimit;			g.time = now;			g.changed = 1;			if(debug)				syslog(0, logfile, sys->sprint("Existing: %s & %s -> %s", g.dest.text(), g.mask.masktext(), g.gateway.text()));			if(iszero(g.dest) && iszero(g.mask)){				defroute = g;				g.local = 1;			}else if(defroute != nil && g.dest.eq(defroute.gateway))				continue;			else				g.local = !ismyaddr(g.gateway);		}	}} unpackroute(b: array of byte, version: int, gwa: IPaddr){	# check that it's an IP route, valid metric, MBZ fields zero 	if(b[0] != byte 0 || b[1] != byte AF_INET){		if(debug > 1)			sys->fprint(sys->fildes(2), "\t-- unknown address type %x,%x\n", int b[0], int b[1]);		return;	}	dest := IPaddr.newv4(b[Oaddr:]);	mask: IPaddr;	if(version == 1){		# check MBZ fields		if(ip->get2(b, 2) | ip->get4(b, Omask) | ip->get4(b, Onexthop)){			if(debug > 1)				sys->fprint(sys->fildes(2), "\t-- non-zero MBZ\n");			return;		}		mask = maskgen(dest);	}else if(version == 2){		if(ip->get4(b, Omask))			mask = IPaddr.newv4(b[Omask:]);		else			mask = maskgen(dest);		if(ip->get4(b, Onexthop))			gwa = IPaddr.newv4(b[Onexthop:]);	}	metric := ip->get4(b, Ometric);	if(debug > 1)		sys->fprint(sys->fildes(2), "\t%s %d\n", dest.text(), metric);	if(metric <= 0 || metric > HopLimit)		return; 	# 1058/3.4.2: response processing	# ignore route if IP address is:	#	class D or E	#	net 0 (except perhaps 0.0.0.0)	#	net 127	#	broadcast address (all 1s host part)	# we allow host routes 	if(dest.ismulticast() || dest.a[0] == byte 0 || dest.a[0] == byte 16r7F){		if(debug > 1)			sys->fprint(sys->fildes(2), "\t%s %d invalid addr\n", dest.text(), metric);		return;	}	if(isbroadcast(dest, mask)){		if(debug > 1)			sys->fprint(sys->fildes(2), "\t%s & %s -> broadcast\n", dest.text(), mask.masktext());		return;	} 	# update the metric min(metric+NetworkCost, HopLimit) 	metric += NetworkCost;	if(metric > HopLimit)		metric = HopLimit; 	updateroute(dest, mask, gwa, metric);} updateroute(dest, mask, gwa: IPaddr, metric: int){	# RFC1058 rules page 27-28, with optional replacement of expiring routes	r := lookup(dest);	if(r.valid){		if(r.local)			return;	# local, don't touch		if(r.gateway.eq(gwa)){			if(metric != HopLimit){				r.metric = metric;				r.time = now;			}else{				# metric == HopLimit				if(r.metric != HopLimit){					r.metric = metric;					r.changed = 1;					r.time = now - (Discard-120);					delroute(r);	# don't use it for routing					# route remains valid but advertised with metric HopLimit				} else if(now >= r.time+Discard){					delroute(r);	# finally dead					r.valid = 0;					r.changed = 1;				}			}		}else if(metric < r.metric ||			  metric != HopLimit && metric == r.metric && now > r.time+Expired/2){			delroute(r);			r.metric = metric;			r.gateway = gwa;			r.time = now;			addroute(r);		}	} else if(metric < HopLimit){	# new entry 		# 1058/3.4.2: don't add route-to-host if host is on net/subnet		# for which we have at least as good a route 		if(!mask.eq(ip->allbits) ||		   ((pr := findroute(dest)) == nil || metric <= pr.metric)){			r.valid = 1;			r.changed = 1;			r.time = now;			r.metric = metric;			r.dest = dest;			r.mask = mask;			r.gateway = gwa;			addroute(r);		}	}} sender(){	for(;;){		sys->sleep(OutputRate*1000);	# could add some random fizz		sendall(OpReply, 1);	}} onlist(a: IPaddr, l: list of IPaddr): int{	for(; l != nil; l = tl l)		if(a.eq(hd l))			return 1;	return 0;} sendall(op: int, changes: int){	for(l := nets; l != nil; l = tl l){		if(addrs != nil && !onlist((hd l).net, addrs))			continue;		a := (hd l).net.copy();		b := (ip->allbits).maskn((hd l).mask);		for(i := 0; i < len a.a; i++)			a.a[i] |= b.a[i];		sendroutes(hd l, a, op, changes);	}	for(i := 0; i < len routes; i++)		if((r := routes[i]) != nil)			r.changed = 0;} zeroentry := array[Ipdestlen] of {* => byte 0}; sendroutes(ifc: ref Ifcaddr, dst: IPaddr, op: int, changes: int){	if(debug > 1)		sys->print("rip: send %s\n", dst.text());	buf := array[Maxripmsg+IP->Udphdrlen] of byte;	hdr := Udphdr.new();	hdr.lport = hdr.rport = RIPport;	hdr.raddr = dst;	# needn't copy	hdr.pack(buf, IP->Udphdrlen);	o := IP->Udphdrlen;	buf[o] = byte op;	buf[o+1] = byte myversion;	buf[o+2] = byte 0;	buf[o+3] = byte 0;	o += Riphdrlen;#	rips := buf[IP->Udphdrlen+Riphdrlen:];	if(op == OpRequest){		buf[o:] = zeroentry;		ip->put4(buf, o+Ometric, HopLimit);		o += Ipdestlen;	} else {		# send routes		for(i:=0; i<len routes; i++){			r := routes[i];			if(r == nil || !r.valid || changes && !r.changed)				continue;			if(r == defroute)				continue;			if(r.dest.eq(ifc.net) || isonnet(r.dest, ifc))				continue;			netmask := r.dest.classmask();			subnet := !r.mask.eq(netmask);			if(myversion < 2 && !r.mask.eq(ip->allbits)){				# if not a host route, don't let a subnet route leave its net				if(subnet && !netmask.eq(ifc.ip.classmask()))					continue;			}			if(o+Ipdestlen > IP->Udphdrlen+Maxripmsg){				if(sys->write(netfd, buf, o) < 0)					sys->fprint(sys->fildes(2), "RIP write failed: %r\n");				o = IP->Udphdrlen + Riphdrlen;			}			buf[o:] = zeroentry;			ip->put2(buf, o+Otype, AF_INET);			buf[o+Oaddr:] = r.dest.v4();			ip->put4(buf, o+Ometric, r.metric);			if(myversion == 2 && subnet)				buf[o+Omask:] = r.mask.v4();			o += Ipdestlen;		}	}	if(o > IP->Udphdrlen+Riphdrlen && sys->write(netfd, buf, o) < 0)		sys->fprint(sys->fildes(2), "rip: network write to %s failed: %r\n", dst.text());} lookup(addr: IPaddr): ref Gateway{	avail := -1;	for(i:=0; i<len routes; i++){		g := routes[i];		if(g == nil || !g.valid){			if(avail < 0)				avail = i;			continue;		}		if(g.dest.eq(addr))			return g;	}	if(avail < 0){		avail = len routes;		a := array[len routes+Routeinc] of ref Gateway;		a[0:] = routes;		routes = a;	}	if((g := routes[avail]) == nil){		g = ref Gateway;		routes[avail] = g;		g.valid = 0;	}	g.dest = addr;	return g;} findroute(a: IPaddr): ref Gateway{	pr: ref Gateway;	for(i:=0; i<len routes; i++){		r := routes[i];

Lines 1–500 of 625.