/* display an address */ char * addrname(naddr addr, /* in network byte order */ naddr mask, int force) /* 0=show mask if nonstandard, */ { /* 1=always show mask, 2=never */ #define NUM_BUFS 4 static int bufno; static struct { char str[15+20]; } bufs[NUM_BUFS]; char *s, *sp; naddr dmask; int i; s = strcpy(bufs[bufno].str, naddr_ntoa(addr)); bufno = (bufno+1) % NUM_BUFS; if (force == 1 || (force == 0 && mask != std_mask(addr))) { sp = &s[strlen(s)]; dmask = mask & -mask; if (mask + dmask == 0) { for (i = 0; i != 32 && ((1<<i) & mask) == 0; i++) continue; sprintf(sp, "/%d", 32-i); } else { sprintf(sp, " (mask %#x)", (u_int)mask); } } return s; #undef NUM_BUFS }
/* display an address */ char * addrname(in_addr_t addr, /* in network byte order */ in_addr_t mask, int force) /* 0=show mask if nonstandard, */ { /* 1=always show mask, 2=never */ #define NUM_BUFS 4 static int bufno; static struct { /* * this array can hold either of the following strings terminated * by a null character: * "xxx.xxx.xxx.xxx/xx" * "xxx.xxx.xxx.xxx (mask xxx.xxx.xxx.xxx)" * */ char str[2*INET_ADDRSTRLEN + sizeof (" (mask )")]; } bufs[NUM_BUFS]; char *s, *sp; in_addr_t dmask; int i, len; struct in_addr tmp_addr; tmp_addr.s_addr = addr; len = strlcpy(bufs[bufno].str, inet_ntoa(tmp_addr), sizeof (bufs[bufno].str)); s = bufs[bufno].str; bufno = (bufno+1) % NUM_BUFS; if (force == 1 || (force == 0 && mask != std_mask(addr))) { sp = &s[strlen(s)]; dmask = mask & -mask; if (mask + dmask == 0) { i = ffs(mask); (void) snprintf(sp, (sizeof (bufs[bufno].str) - len), "/%d", (NBBY * sizeof (in_addr_t) + 1) - i); } else { (void) snprintf(sp, (sizeof (bufs[bufno].str) - len), " (mask %s)", naddr_ntoa(htonl(mask))); } } return (s); #undef NUM_BUFS }
/* Supply dst with the contents of the routing tables. * If this won't fit in one packet, chop it up into several. */ void supply(struct sockaddr_in *dst, struct interface *ifp, /* output interface */ enum output_type type, int flash, /* 1=flash update */ int vers, /* RIP version */ int passwd_ok) /* OK to include cleartext password */ { struct rt_entry *rt; int def_metric; ws.state = 0; ws.gen_limit = 1024; ws.to = *dst; ws.to_std_mask = std_mask(ws.to.sin_addr.s_addr); ws.to_std_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_std_mask; if (ifp != NULL) { ws.to_mask = ifp->int_mask; ws.to_net = ifp->int_net; if (on_net(ws.to.sin_addr.s_addr, ws.to_net, ws.to_mask)) ws.state |= WS_ST_TO_ON_NET; } else { ws.to_mask = ripv1_mask_net(ws.to.sin_addr.s_addr, 0); ws.to_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_mask; rt = rtfind(dst->sin_addr.s_addr); if (rt) ifp = rt->rt_ifp; } ws.npackets = 0; if (flash) ws.state |= WS_ST_FLASH; if ((ws.ifp = ifp) == NULL) { ws.metric = 1; } else { /* Adjust the advertised metric by the outgoing interface * metric. */ ws.metric = ifp->int_metric+1; } ripv12_buf.rip.rip_vers = vers; switch (type) { case OUT_MULTICAST: if (ifp->int_if_flags & IFF_MULTICAST) v2buf.type = OUT_MULTICAST; else v2buf.type = NO_OUT_MULTICAST; v12buf.type = OUT_BROADCAST; break; case OUT_QUERY: ws.state |= WS_ST_QUERY; /* fall through */ case OUT_BROADCAST: case OUT_UNICAST: v2buf.type = (vers == RIPv2) ? type : NO_OUT_RIPV2; v12buf.type = type; break; case NO_OUT_MULTICAST: case NO_OUT_RIPV2: break; /* no output */ } if (vers == RIPv2) { /* full RIPv2 only if cannot be heard by RIPv1 listeners */ if (type != OUT_BROADCAST) ws.state |= WS_ST_RIP2_ALL; if ((ws.state & WS_ST_QUERY) || !(ws.state & WS_ST_TO_ON_NET)) { ws.state |= (WS_ST_AG | WS_ST_SUPER_AG); } else if (ifp == NULL || !(ifp->int_state & IS_NO_AG)) { ws.state |= WS_ST_AG; if (type != OUT_BROADCAST && (ifp == NULL || !(ifp->int_state & IS_NO_SUPER_AG))) ws.state |= WS_ST_SUPER_AG; } } ws.a = (vers == RIPv2) ? find_auth(ifp) : 0; if (!passwd_ok && ws.a != NULL && ws.a->type == RIP_AUTH_PW) ws.a = NULL; clr_ws_buf(&v12buf,ws.a); clr_ws_buf(&v2buf,ws.a); /* Fake a default route if asked and if there is not already * a better, real default route. */ if (supplier && (def_metric = ifp->int_d_metric) != 0) { if (NULL == (rt = rtget(RIP_DEFAULT, 0)) || rt->rt_metric+ws.metric >= def_metric) { ws.state |= WS_ST_DEFAULT; ag_check(0, 0, 0, 0, def_metric, def_metric, 0, 0, 0, supply_out); } else { def_metric = rt->rt_metric+ws.metric; } /* If both RIPv2 and the poor-man's router discovery * kludge are on, arrange to advertise an extra * default route via RIPv1. */ if ((ws.state & WS_ST_RIP2_ALL) && (ifp->int_state & IS_PM_RDISC)) { ripv12_buf.rip.rip_vers = RIPv1; v12buf.n->n_family = RIP_AF_INET; v12buf.n->n_dst = htonl(RIP_DEFAULT); v12buf.n->n_metric = htonl(def_metric); v12buf.n++; } } rn_walktree(rhead, walk_supply, 0); ag_flush(0,0,supply_out); /* Flush the packet buffers, provided they are not empty and * do not contain only the password. */ if (v12buf.n != v12buf.base && (v12buf.n > v12buf.base+1 || v12buf.base->n_family != RIP_AF_AUTH)) supply_write(&v12buf); if (v2buf.n != v2buf.base && (v2buf.n > v2buf.base+1 || v2buf.base->n_family != RIP_AF_AUTH)) supply_write(&v2buf); /* If we sent nothing and this is an answer to a query, send * an empty buffer. */ if (ws.npackets == 0 && (ws.state & WS_ST_QUERY)) supply_write(&v12buf); }
/* * Read a list of gateways from /etc/gateways and add them to our tables. * * This file contains a list of "remote" gateways. That is usually * a gateway which we cannot immediately determine if it is present or * not as we can do for those provided by directly connected hardware. * * If a gateway is marked "passive" in the file, then we assume it * does not understand RIP and assume it is always present. Those * not marked passive are treated as if they were directly connected * and assumed to be broken if they do not send us advertisements. * All remote interfaces are added to our list, and those not marked * passive are sent routing updates. * * A passive interface can also be local, hardware interface exempt * from RIP. */ void gwkludge(void) { #define STR2(x) #x #define STR(x) STR2(x) #define NETHOST_LEN 4 #define DNAME_LEN MAXHOSTNAMELEN #define GNAME_LEN MAXHOSTNAMELEN #define QUAL_LEN 8 FILE *fp; char *p, *lptr; const char *cp; char lbuf[PARMS_MAXLINELEN], net_host[NETHOST_LEN + 1]; char dname[MAXHOSTNAMELEN + 1]; char gname[MAXHOSTNAMELEN + 1], qual[QUAL_LEN +1]; struct interface *ifp; uint32_t dst, netmask, gate; int n; uint32_t lnum; struct stat sb; uint32_t state, metric; boolean_t default_dst; fp = fopen(PATH_GATEWAYS, "r"); if (fp == NULL) return; if (0 > fstat(fileno(fp), &sb)) { msglog("fstat() failed: %s for "PATH_GATEWAYS, rip_strerror(errno)); (void) fclose(fp); return; } for (lnum = 1; ; lnum++) { if (NULL == fgets(lbuf, sizeof (lbuf), fp)) break; /* Eliminate the /n character at the end of the lbuf */ if (strlen(lbuf) > 0) lbuf[strlen(lbuf) - 1] = '\0'; /* Move lptr to the first non-space character */ for (lptr = lbuf; isspace(*lptr); lptr++) ; if (*lptr == '#' || *lptr == '\0') continue; /* Move p to the end of the line */ p = lptr + strlen(lptr) - 1; /* Skip all trailing spaces except escaped space */ while (p > lptr && (isspace(*p) && *(p-1) != '\\')) p--; /* truncate the line to remove trailing spaces */ *++p = '\0'; /* notice newfangled parameter lines */ if (strncasecmp("net", lptr, 3) != 0 && strncasecmp("host", lptr, 4) != 0) { cp = parse_parms(lptr, (sb.st_uid == 0 && !(sb.st_mode&(S_IRWXG|S_IRWXO)))); if (cp != 0) msglog("%s in line %u of "PATH_GATEWAYS, cp, lnum); continue; } /* * Processes lines of the follwoing format: * net|host <name>[/mask] gateway <Gname> metric <value> * passive|active|extern */ qual[0] = '\0'; n = sscanf(lptr, "%"STR(NETHOST_LEN)"s %"STR(DNAME_LEN) "[^ \t] gateway %"STR(GNAME_LEN)"[^ / \t] metric %u %" STR(QUAL_LEN)"s\n", net_host, dname, gname, &metric, qual); if (n != 4 && n != 5) { msglog("bad "PATH_GATEWAYS" entry \"%s\"; %d values", lptr, n); continue; } if (metric >= HOPCNT_INFINITY) { msglog("bad metric in "PATH_GATEWAYS" entry \"%s\"", lptr); continue; } default_dst = _B_FALSE; if (strcasecmp(net_host, "host") == 0) { if (!gethost(dname, &dst)) { msglog("bad host \"%s\" in "PATH_GATEWAYS " entry \"%s\"", dname, lptr); continue; } netmask = HOST_MASK; } else if (strcasecmp(net_host, "net") == 0) { if (!getnet(dname, &dst, &netmask)) { msglog("bad net \"%s\" in "PATH_GATEWAYS " entry \"%s\"", dname, lptr); continue; } default_dst = (dst == RIP_DEFAULT); dst = htonl(dst); /* make network # into IP address */ } else { msglog("bad \"%s\" in "PATH_GATEWAYS " entry \"%s\"", net_host, lptr); continue; } if (!gethost(gname, &gate)) { msglog("bad gateway \"%s\" in "PATH_GATEWAYS " entry \"%s\"", gname, lptr); continue; } if (strcasecmp(qual, "passive") == 0) { /* * Passive entries are not placed in our tables, * only the kernel's, so we don't copy all of the * external routing information within a net. * Internal machines should use the default * route to a suitable gateway (like us). */ state = IS_REMOTE | IS_PASSIVE; if (metric == 0) metric = 1; } else if (strcasecmp(qual, "external") == 0) { /* * External entries are handled by other means * such as EGP, and are placed only in the daemon * tables to prevent overriding them with something * else. */ (void) strlcpy(qual, "external", sizeof (qual)); state = IS_REMOTE | IS_PASSIVE | IS_EXTERNAL; if (metric == 0) metric = 1; } else if (strcasecmp(qual, "active") == 0 || qual[0] == '\0') { if (default_dst) { msglog("bad net \"%s\" in "PATH_GATEWAYS " entry \"%s\"-- cannot be default", dname, lptr); continue; } if (metric != 0) { /* * Entries that are neither "passive" nor * "external" are "remote" and must behave * like physical interfaces. If they are not * heard from regularly, they are deleted. */ state = IS_REMOTE; } else { /* * "remote" entries with a metric of 0 * are aliases for our own interfaces */ state = IS_REMOTE | IS_PASSIVE | IS_ALIAS; } } else { msglog("bad "PATH_GATEWAYS" entry \"%s\";" " unknown type %s", lptr, qual); continue; } if (0 != (state & (IS_PASSIVE | IS_REMOTE))) state |= IS_NO_RDISC; if (state & IS_PASSIVE) state |= IS_NO_RIP; if (default_dst) { addroutefordefault(dst, gate, netmask, metric, ((state & IS_EXTERNAL)? RTS_EXTERNAL : 0)); continue; } ifp = check_dup(NULL, gate, dst, netmask, 0, _B_FALSE); if (ifp != NULL) { msglog("duplicate "PATH_GATEWAYS" entry \"%s\"", lptr); continue; } ifp = rtmalloc(sizeof (*ifp), "gwkludge()"); (void) memset(ifp, 0, sizeof (*ifp)); ifp->int_state = state; if (netmask == HOST_MASK) ifp->int_if_flags = IFF_POINTOPOINT | IFF_UP; else ifp->int_if_flags = IFF_UP; ifp->int_act_time = NEVER; ifp->int_addr = gate; ifp->int_dstaddr = dst; ifp->int_mask = netmask; ifp->int_ripv1_mask = netmask; ifp->int_std_mask = std_mask(gate); ifp->int_net = ntohl(dst); ifp->int_std_net = ifp->int_net & ifp->int_std_mask; ifp->int_std_addr = htonl(ifp->int_std_net); ifp->int_metric = metric; if (!(state & IS_EXTERNAL) && ifp->int_mask != ifp->int_std_mask) ifp->int_state |= IS_SUBNET; (void) snprintf(ifp->int_name, sizeof (ifp->int_name), "remote(%s)", gname); if_link(ifp, 0); } (void) fclose(fp); /* * After all of the parameter lines have been read, * apply them to any remote interfaces. */ for (ifp = ifnet; NULL != ifp; ifp = ifp->int_next) { get_parms(ifp); tot_interfaces++; if (!IS_RIP_OFF(ifp->int_state)) rip_interfaces++; if (!IS_RIP_OUT_OFF(ifp->int_state)) ripout_interfaces++; trace_if("Add", ifp); } }
/* * Handle an incoming RIP packet. */ static void rip_input(struct sockaddr_in *from, int size, uint_t ifindex) { struct netinfo *n, *lim; struct in_addr in; const char *name; char net_buf[80]; uchar_t hash[RIP_AUTH_MD5_LEN]; MD5_CTX md5_ctx; uchar_t md5_authed = 0; in_addr_t mask, dmask; struct in_addr tmp_addr; char *sp; char ifname[IF_NAMESIZE+1]; int i; struct hostent *hp; struct netent *np; struct netauth *na; char srcaddr[MAXHOSTNAMELEN + sizeof (" (123.123.123.123)") + 1]; char ifstring[IF_NAMESIZE + 3*sizeof (ifindex) + sizeof (" ()") + 1]; if (!nflag && (hp = gethostbyaddr((char *)&from->sin_addr, sizeof (struct in_addr), AF_INET)) != NULL) { (void) snprintf(srcaddr, sizeof (srcaddr), "%s (%s)", hp->h_name, inet_ntoa(from->sin_addr)); } else { /* safe; cannot overflow destination */ (void) strcpy(srcaddr, inet_ntoa(from->sin_addr)); } if (ifindex == 0) { (void) printf("%s:", srcaddr); } else { if (if_indextoname(ifindex, ifname) != NULL) (void) snprintf(ifstring, sizeof (ifstring), "%s (%d)", ifname, ifindex); else (void) snprintf(ifstring, sizeof (ifstring), "%d", ifindex); (void) printf(gettext("%1$s received on interface %2$s:"), srcaddr, ifstring); } if (IMSG.rip_cmd != RIPCMD_RESPONSE) { (void) printf(gettext("\n unexpected response type %d\n"), IMSG.rip_cmd); return; } (void) printf(gettext(" RIPv%1$d%2$s %3$d bytes\n"), IMSG.rip_vers, (IMSG.rip_vers != RIPv1 && IMSG.rip_vers != RIPv2) ? " ?" : "", size); if (size > MAXPACKETSIZE) { if (size > sizeof (imsg_buf) - sizeof (*n)) { (void) printf( gettext(" at least %d bytes too long\n"), size-MAXPACKETSIZE); size = sizeof (imsg_buf) - sizeof (*n); } else { (void) printf(gettext(" %d bytes too long\n"), size-MAXPACKETSIZE); } } else if (size%sizeof (*n) != sizeof (struct rip)%sizeof (*n)) { (void) printf(gettext(" response of bad length=%d\n"), size); } n = IMSG.rip_nets; lim = n + (size - 4) / sizeof (struct netinfo); for (; n < lim; n++) { name = ""; if (n->n_family == RIP_AF_INET) { in.s_addr = n->n_dst; (void) strlcpy(net_buf, inet_ntoa(in), sizeof (net_buf)); tmp_addr.s_addr = (n->n_mask); mask = ntohl(n->n_mask); dmask = mask & -mask; if (mask != 0) { sp = &net_buf[strlen(net_buf)]; if (IMSG.rip_vers == RIPv1) { (void) snprintf(sp, (sizeof (net_buf) - strlen(net_buf)), gettext(" mask=%s ? "), inet_ntoa(tmp_addr)); mask = 0; } else if (mask + dmask == 0) { i = ffs(mask) - 1; (void) snprintf(sp, (sizeof (net_buf) - strlen(net_buf)), "/%d", 32-i); } else { (void) snprintf(sp, (sizeof (net_buf) - strlen(net_buf)), gettext(" (mask %s)"), inet_ntoa(tmp_addr)); } } if (!nflag) { if (mask == 0) { mask = std_mask(in.s_addr); if ((ntohl(in.s_addr) & ~mask) != 0) mask = 0; } /* * Without a netmask, do not worry about * whether the destination is a host or a * network. Try both and use the first name * we get. * * If we have a netmask we can make a * good guess. */ if ((in.s_addr & ~mask) == 0) { np = getnetbyaddr((long)in.s_addr, AF_INET); if (np != NULL) name = np->n_name; else if (in.s_addr == 0) name = "default"; } if (name[0] == '\0' && ((in.s_addr & ~mask) != 0 || mask == 0xffffffff)) { hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET); if (hp != NULL) name = hp->h_name; } } } else if (n->n_family == RIP_AF_AUTH) { na = (struct netauth *)n; if (na->a_type == RIP_AUTH_PW && n == IMSG.rip_nets) { (void) printf( gettext(" Password Authentication:" " \"%s\"\n"), qstring(na->au.au_pw, RIP_AUTH_PW_LEN)); continue; } if (na->a_type == RIP_AUTH_MD5 && n == IMSG.rip_nets) { (void) printf(gettext(" MD5 Auth" " len=%1$d KeyID=%2$d" " auth_len=%3$d" " seqno=%4$#x" " rsvd=%5$#x,%6$#x\n"), ntohs(na->au.a_md5.md5_pkt_len), na->au.a_md5.md5_keyid, na->au.a_md5.md5_auth_len, (int)ntohl(na->au.a_md5.md5_seqno), na->au.a_md5.rsvd[0], na->au.a_md5.rsvd[1]); md5_authed = 1; continue; } (void) printf(gettext(" Authentication type %d: "), ntohs(na->a_type)); for (i = 0; i < sizeof (na->au.au_pw); i++) (void) printf("%02x ", na->au.au_pw[i]); (void) putchar('\n'); if (md5_authed && n+1 > lim && na->a_type == RIP_AUTH_TRAILER) { MD5Init(&md5_ctx); MD5Update(&md5_ctx, (uchar_t *)&IMSG, (char *)na-(char *)&IMSG); MD5Update(&md5_ctx, (uchar_t *)passwd, RIP_AUTH_MD5_LEN); MD5Final(hash, &md5_ctx); (void) printf(gettext(" %s hash\n"), memcmp(hash, na->au.au_pw, sizeof (hash)) ? gettext("WRONG") : gettext("correct")); } else if (md5_authed && n+1 > lim && na->a_type != RIP_AUTH_TRAILER) { (void) printf(gettext("Error -" "authentication entry missing hash\n")); } continue; } else { tmp_addr.s_addr = n->n_dst; (void) snprintf(net_buf, sizeof (net_buf), gettext("(address family %1$u) %2$s"), ntohs(n->n_family), inet_ntoa(tmp_addr)); } (void) printf(gettext(" %1$-18s metric %2$2lu %3$-10s"), net_buf, ntohl(n->n_metric), name); if (n->n_nhop != 0) { in.s_addr = n->n_nhop; if (nflag) hp = NULL; else hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET); (void) printf(gettext(" nhop=%1$-15s%2$s"), (hp != NULL) ? hp->h_name : inet_ntoa(in), (IMSG.rip_vers == RIPv1) ? " ?" : ""); } if (n->n_tag != 0) (void) printf(gettext(" tag=%1$#x%2$s"), n->n_tag, (IMSG.rip_vers == RIPv1) ? " ?" : ""); (void) putchar('\n'); } }