/* * Copyright (c) 2000 Columbia University * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by Columbia University * and its contributors.'' Neither the name of the University nor the names * of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Written by Ping Pan, Columbia University/Bell Labs, 2000 * * The author would like to thank Bell Labs for providing time and freedom * to complete this work. Please forward bug fixes, enhancements and questions * to pingpan@cs.columbia.edu. */ /* yessird: a daeom to support YESSIR, a light-weight reservation * protocol for real-time streams. * * For more information on YESSIR, check * http://www.cs.columbia.edu/~pingpan/projects/yessir.html * * Quick intro: YESSIR is not designed as a generic reservation * protocol for all traffic (though may sound more sexy that way). * It makes a best-effort attempt to reserve network resource * for real-time traffic that uses RTP to transport data. * Reservation is carried inside the RTCP messages. * * Sorry, I don't believe email and WEB downloads need user-level * resource reservation from the network. If users have trouble * with their WEB traffic, contact the ISP's to configurate the network * better. Only real-time traffic really deserves special treatment * as far as users are concerned. * * Notes * ----- * This program must be run by root or be setuid. Technically, * raw sockets require superuser priveleges. As a common sense, * only superusers should have the right to run this program * to grab or to release network resources. * * This program requires a kernel mod that does not appear in any * availeable UNIX system: A raw socket with a new protocol family, * IPOPT (ip option), is used to intercept the data on all interfaces. * The data packets can be any IP protocol. For more detail, check * http://www.cs.columbia.edu/~pingpan/software/netipopt * * YESSIR uses the RTCP messages to make reservation decision, but during * parsing, it may drop the messages with real bad format. Other than that, * NO dropping and re-ordering to the orginal packets. * * There is a limitation on my clock management routines (for soft-state * refresh): while yessird() is running, don't change time od the day... for now. * * The physical interfaces are maganed in a hashed table (index via if-index) * for performance reason. In some low-end routers, we should expect hundreads * of interfaces (channelized T1, Frame Relay DLCI virtual interface....), * so this is not an over-kill. * * I have assumed that the routers support "hot-plug" feature. Also when a new * interface is inserted, the system would assign a new if-index to it. * Given I am writing and testing the code on a PC, the interface up/down * routines (rif_up() and rif_down()) were not tested in all cases. * * To port this code into a platform other than UNIX, replace all the routines in * this file. * * - Ping Pan, March 18, 2000, */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "queue.h" #include "table.h" #include "yestat.h" #include "yes_var.h" int lockfd; /* use for daemon check */ fd_set fdset; /* the fd used in select() */ int sock_max; /* max number of sockets */ int yes_sock; /* YESSIR receive socket */ int send_sock; /* YESSIR sending socket */ u_char *yes_ctrl; /* yessir socket control data pointer */ u_char *yes_buf; /* buffer for yessir message */ int rt_sock; /* route socket */ int rt_seqno; /* route query sequence number */ u_char *rt_buf; /* route query buffer */ int stat_sock; /* yestst() socket */ u_char *stat_buf; /* buffer for yestat message */ char *program_name; /* dummy used in sysctl() */ static struct sockaddr sa_zero = {sizeof(struct sockaddr), AF_INET}; extern char *optarg; int main(int argc, char *argv[]) { register int n, uid, pid; register int debug = 0; register int errors = 0; struct timeval clk; char *tracename = 0, *cp; fd_set curr_fdset; setlinebuf(stdout); setuid(getuid()); if ((uid = getuid())) { perror("need to be a superuser"); exit(1); } if ((cp = strrchr(argv[0], '/')) != NULL) program_name = cp + 1; else program_name = argv[0]; if (!yes_timerinit()) { perror("cannot init the timers"); exit(1); } while ((n = getopt(argc, argv, "dT:")) != -1) { switch (n) { case 'd': debug++; break; case 'T': tracename = optarg; break; case '?': default: errors++; break; } } argc -= optind; argv += optind; if (tracename == 0 && argc >= 1) { tracename = *argv++; argc--; } if (tracename != 0 && tracename[0] == '\0') yes_usage(); if (argc != 0 || errors) yes_usage(); if ((n = yes_alive())) /* daemon exists ? */ logbad(0, "%s: already running with PID %d", program_name, n); if ((!debug) && (daemon(1,0) == -1)) logbad(1, "%s: cannot exec the daemon.", program_name); signal(SIGPIPE, sigpipe); signal(SIGINT, sigint); signal(SIGTERM, sigterm); if (tracename != 0) { strncpy(inittracename, tracename, sizeof(inittracename)-1); set_tracefile(inittracename, "%s\n", -1); } pid = getpid(); if (!yes_savepid(pid)) logbad(1, "%s: cannot save the pid %d.", program_name, pid); if (!yes_tabinit()) logbad(1, "%s: failed on table init.", program_name); if (!rif_check()) logbad(1, "%s: failed on interface init.", program_name); if (!sockinit()) logbad(1, "%s: failed on socket init.", program_name); if (!yes_tcinit()) logbad(1, "%s: failed on TC init.", program_name); for (;;) { yestime->last = yestime->now; gettimeofday(&yestime->now, 0); /* check flow states */ timevalsub(&clk, &yestime->now, &yestime->next); if (clk.tv_sec < 0) yesrefresh(); /* check interfaces */ timevalsub(&clk, &yestime->now, &rifm->chk_timer); if (clk.tv_sec < 0) rif_check(); clk.tv_sec = 1; clk.tv_usec = 0; curr_fdset = fdset; n = select(sock_max, &curr_fdset, 0, 0, &clk); if (n <= 0) { if (n < 0 && errno != EINTR && errno != EAGAIN) /* could happen in old PSOS */ logbad(0, "bad select"); continue; } if (FD_ISSET(yes_sock, &curr_fdset)) { yes_sockread(yes_sock); n--; } if (FD_ISSET(stat_sock, &curr_fdset)) { stat_sockread(stat_sock); n--; } } } /* signaling handlers... */ void nosir() { sockreset(); unlink(YES_PID); return; } void sigpipe(int sig) { msglog("yessid: got a SIGPIPE, %d", sig); signal(SIGPIPE, sigpipe); } void sigint(int sig) { msglog("yessid: exit on a SIGINT"); nosir(); _exit(sig); } void sigterm(int sig) { msglog("yessid: exit on a SIGTERM"); nosir(); _exit(sig); } /* Check to see if daemon is already running.... * Return: * 0 OK * others not OK */ int yes_alive() { struct sockaddr_in addr; FILE *fd; int pid; memset((char *)(&addr), 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = YES_LOCK_PORT; addr.sin_len = sizeof(struct sockaddr_in); lockfd = socket(PF_INET, SOCK_DGRAM, PF_UNSPEC); if (lockfd == -1) return -1; if(!bind(lockfd, (struct sockaddr *)&addr, sizeof(addr))) return 0; close(lockfd); if ((fd = fopen(YES_PID, "r")) == NULL) return -1; if (fscanf(fd, "%u", &pid)) return -1; fclose(fd); return pid; } int yes_savepid(int pid) { FILE *fp; if (unlink(YES_PID) == -1) { if (errno != ENOENT) { return 0; } } if ((fp = fopen(YES_PID, "w")) == NULL) return 0; fprintf(fp, "%u\n", pid); fclose(fp); return -1; } void yes_usage() { fprintf(stderr, "%s [-d] [-T tracefile]\n", program_name); exit(1); } /* Create and modify sockets to receive RTCP messages with * IP alert option and received interface index. Also create the sockets * for route query, sending packets and yestat(). */ /* yessir socket config for rx and tx. */ int yes_sockinit() { int on=1, rc; /* setup rx. socket */ if ((yes_sock = socket(PF_IPOPTION, SOCK_RAW, IPOPT_RA)) < 0) { logbad(0, "yes_sock: socket() < 0"); return 0; } if (fcntl(yes_sock, F_SETFL, O_NONBLOCK) == -1) { logbad(0, "fcntl failed to O_NONBLOCK: %s", errno); return 0; } /* to receive RTCP messages */ rc = setsockopt(yes_sock, IPPROTO_IP, IPOPT_RECVRTCP, &on, sizeof(on)); if (rc < 0) { logbad(0, "setsockopt() for RTCP < 0"); return 0; } /* always get ingress interface data */ rc = setsockopt(yes_sock, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); if (rc < 0) { logbad(0, "setsockopt() for RECVIF < 0"); return 0; } /* get control data and receiving buffer space */ if (!(yes_ctrl = (u_char *)malloc(YES_MAXCTRLSIZE))) logbad(1, "cannot get yessir control data space"); if (!(yes_buf = (u_char *)malloc(YES_MAXPKTSIZE))) logbad(1, "cannot get yessir input buffer"); FD_SET(yes_sock, &fdset); /* setup tx. socket */ if ((send_sock = socket(PF_INET, SOCK_RAW, 0)) < 0) { logbad(0, "send_sock: socket() < 0"); return 0; } rc = setsockopt(send_sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)); if (rc < 0) { logbad(0, "setsockopt() for IP_HDRINCL < 0"); return 0; } return -1; } /* route socket */ int rt_sockinit() { if ((rt_sock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { logbad(0, "rt_sock: socket() < 0"); return 0; } rt_buf = (u_char *)malloc(YES_RTBUFLEN); if (!rt_buf) { logbad(0, "cannot get route query space"); return 0; } rt_seqno = 1; return -1; } /* yestat socket */ int stat_sockinit() { struct sockaddr_in dest; int on=1, bufsz, cc, rc; if ((stat_sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { logbad(0, "stat_sock: socket() < 0"); return 0; } if (fcntl(stat_sock, F_SETFL, O_NONBLOCK) == -1) logbad(1, "fcntl failed to O_NONBLOCK: %s", errno); rc = setsockopt(stat_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (rc < 0) { logbad(0, "setsockopt() for SO_REUSEADDR < 0"); return 0; } rc = setsockopt(stat_sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); if (rc < 0) { logbad(0, "setsockopt() for SO_REUSEPORT < 0"); return 0; } cc = sizeof(bufsz); if (getsockopt(stat_sock, SOL_SOCKET, SO_SNDBUF, &bufsz, &cc) < 0) { logbad(0, "yestat: fail to getsockopt on SO_SNDBUF"); return 0; } if (bufsz < YESTAT_MAXBUF) { bufsz = YESTAT_MAXBUF; rc = setsockopt(stat_sock, SOL_SOCKET, SO_SNDBUF, &bufsz, cc); if (rc < 0) { logbad(0, "setsockopt() for SO_SNDBUF < 0"); return 0; } } memset((char *)(&dest), 0, sizeof(struct sockaddr_in)); dest.sin_family = PF_INET; dest.sin_addr.s_addr = INADDR_ANY; dest.sin_port = htons(YESSIR_STAT_PORT); dest.sin_len = sizeof(struct sockaddr_in); if (bind(stat_sock, (struct sockaddr *)&dest, sizeof(dest))) { logbad(0, "bind() for stat_sock failed"); return 0; } if (!(stat_buf = (u_char *)malloc(YESTAT_MAXBUF))) logbad(1, "cannot get buffer for yestat"); FD_SET(stat_sock, &fdset); return -1; } int sockinit() { sock_max = getdtablesize(); FD_ZERO(&fdset); if (!yes_sockinit()) return 0; if (!rt_sockinit()) return 0; if (!stat_sockinit()) return 0; return -1; } /* Returning all the space... actually makes no sense in UNIX */ void sockreset() { FD_CLR(yes_sock, &fdset); FD_CLR(stat_sock, &fdset); /* close all sockets */ if (close(yes_sock) == -1) logbad(0, "cannot close yes_sock"); if (close(send_sock) == -1) logbad(0, "cannot close send_sock"); if (close(rt_sock) == -1) logbad(0, "cannot close rt_sock"); if (close(stat_sock) == -1) logbad(0, "cannot close stat_sock"); /* free buffers */ free(yes_ctrl); free(yes_buf); free(rt_buf); free(stat_buf); return; } /* yessir socket I/O */ void yes_sockread(int sock) { struct msghdr msg; struct sockaddr_in from; struct iovec iov; struct cmsghdr *cmsg; int len, if_index, local; if_index = -1; local = -1; for (;;) { bzero(&msg, sizeof(msg)); msg.msg_name = (caddr_t)&from; msg.msg_namelen = sizeof(from); msg.msg_control = (char *)yes_ctrl; msg.msg_controllen = YES_MAXCTRLSIZE; msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = (char *)yes_buf; iov.iov_len = YES_MAXPKTSIZE; if ((len = recvmsg(sock, &msg, 0)) < 0) break; if (len > YES_MAXPKTSIZE) { logbad(0, "rcv'd too large %d", len); break; } cmsg = CMSG_FIRSTHDR(&msg); for (; cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_type == IP_RECVIF) if_index = CMSG_IFINDEX(cmsg); if (cmsg->cmsg_type == IPOPT_RECVLOCAL) local = CMSG_LOCAL(cmsg); } /* something is wrong, don't take chances 'cause looping */ if (local == -1) continue; yessir_read(yes_buf, len, if_index, local); } return; } void yes_send(char *msg, int lgt, struct in_addr *dst) { struct sockaddr whereto; struct sockaddr_in *to = (struct sockaddr_in *)&whereto; yestat->rtcp_tx_pkt++; to->sin_len = sizeof(sin); to->sin_family = PF_INET; to->sin_addr.s_addr = dst->s_addr; if (sendto(send_sock, msg, lgt, 0, &whereto, sizeof(whereto)) < 0) msglog("fail to send YESSIR packets"); return; } /* yestat socket I/O */ void stat_sockread(int sock) { struct sockaddr from; int cc, len; len = sizeof(from); for (;;) { cc = recvfrom(sock, stat_buf, YESTAT_MAXBUF, 0, &from, &len); if (cc < 0) break; if (cc > YESTAT_MAXBUF) logbad(1, "stat req rcv'd too large %d", cc); yestat_read(stat_buf, &from); } return; } void stat_send(char *msg, int lgt, struct sockaddr *to) { if (sendto(stat_sock, msg, lgt, 0, to, sizeof(*to)) < 0) msglog("fail to send a yestat packet"); return; } /* Unicast routing query from kernel * * Note: check returned RTAX_DST entry to find out dest unreachable info. * The kernel should return ENETUNREACH in rtm->rtm_errno instead.... * FreeBSD 3.3 does not do that. * * return: * positive number, egress if index * -1, failed */ int yes_rtquery(struct in_addr *dst) { struct rt_msghdr *rtm; struct sockaddr_in *sin; pid_t pid; ssize_t n; memset((char *)(rt_buf), 0, YES_RTBUFLEN); /* write common header: */ rtm = (struct rt_msghdr *)rt_buf; rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in); rtm->rtm_version = RTM_VERSION; rtm->rtm_type = RTM_GET; rtm->rtm_addrs = RTA_DST | RTA_IFP | RTA_IFA; rtm->rtm_pid = pid = getpid(); rtm->rtm_seq = rt_seqno; /* write to index RTAX_DST (0): */ sin = (struct sockaddr_in *)(rtm + 1); sin->sin_len = sizeof(struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_addr.s_addr = dst->s_addr; write(rt_sock, rtm, rtm->rtm_msglen); do { n = read(rt_sock, rtm, YES_RTBUFLEN); } while (rtm->rtm_type != RTM_GET || rtm->rtm_seq != rt_seqno || rtm->rtm_pid != pid); rt_seqno++; rtm = (struct rt_msghdr *)rt_buf; /* read from index RTAX_DST */ sin = (struct sockaddr_in *) (rtm + 1); if (sin->sin_addr.s_addr == 0) /* no route! */ return -1; return rtm->rtm_index; } /* Interface management: * * The following routines are used to monitor interfaces and to manage * link bandwidth. 'rif' stands for Resource-related InterFace. * Actually, this is somewhat independent from the YESSIR protocol itself. * */ /* query the system to detect the interfaces */ int rif_check() { struct timeval clk; size_t sysctl_buf_size, needed; struct if_msghdr *ifm, *ifm_last, *ifm_next; char *sysctl_buf; int i, j, if_index, mib[6]; RIF *rif; if (!rifm) { /* init the rif table: * - get the memory and clean it up */ rifm = (RIFM *)malloc(sizeof(RIFM)); if (rifm == NULL) { logbad(1, "rif_check: no memory"); return 0; } memset((char *)rifm, 0, sizeof(RIFM)); } else { for (i = 0; i < RIF_HASH_SIZE; i++) { j = 0; rif = (RIF *)rif_at(i); for (; j < rif_num(i); j++, rif = rif->next) rif->test = RIF_CHECK; } } /* reset for the next check-up */ gettimeofday(&clk, 0); rifm->chk_timer.tv_sec = clk.tv_sec + RIF_CHECK_INTERVAL; /* fetch the interface list */ sysctl_buf_size = 0; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_IFLIST; mib[5] = 0; for (;;) { if ((needed = sysctl_buf_size) != 0) { if (sysctl(mib, 6, sysctl_buf, &needed, 0, 0) >= 0) break; if (errno != ENOMEM && errno != EFAULT) logbad(1, "rif_check: get interface table"); free(sysctl_buf); needed = 0; } if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) logbad(1,"ifinit: route-sysctl-estimate"); sysctl_buf_size = needed; sysctl_buf = (char *)malloc(needed); if (sysctl_buf == NULL) logbad(1,"ifinit: cannot get memory"); } /* add/modify to our own table */ ifm_last = (struct if_msghdr *)(sysctl_buf + needed); ifm = (struct if_msghdr *)sysctl_buf; for(; ifm < ifm_last; ifm = ifm_next) { ifm_next = (struct if_msghdr*)((char*)ifm + ifm->ifm_msglen); if_index = ifm->ifm_index; rif = rif_get(if_index); if (!rif) { if (!(rif = rif_add(if_index))) return 0; } switch(ifm->ifm_type) { case RTM_IFINFO: rif_up(rif, ifm); rif->test = RIF_OK; continue; case RTM_NEWADDR: rif->test = RIF_OK; rif_change(rif, (struct ifa_msghdr *)ifm); continue; default: logbad(1, "rif_check: out of sync"); continue; } } /* remove the dead interfaces */ for (i = 0; i < RIF_HASH_SIZE; i++) { j = 0; rif = (RIF *)rif_at(i); for (; j < rif_num(i); j++, rif = rif->next) { if (rif->test == RIF_CHECK) rif_down(rif); } } return -1; } /* called when recived RTM_IFINFO */ void rif_up(RIF *rif, struct if_msghdr *ifm) { struct sockaddr_dl *sdl; rif->if_flags = ifm->ifm_flags; rif->if_type = ifm->ifm_data.ifi_type; rif->if_hdrlen = ifm->ifm_data.ifi_hdrlen; rif->if_addrlen = ifm->ifm_data.ifi_addrlen; rif->if_physical = ifm->ifm_data.ifi_physical; rif->if_mtu = ifm->ifm_data.ifi_mtu; rif->link_bw = ifm->ifm_data.ifi_baudrate; rif->rx_pkt = ifm->ifm_data.ifi_ipackets; rif->rx_byte = ifm->ifm_data.ifi_ibytes; rif->rx_err = ifm->ifm_data.ifi_ierrors; rif->tx_pkt = ifm->ifm_data.ifi_opackets; rif->tx_byte = ifm->ifm_data.ifi_obytes; rif->tx_err = ifm->ifm_data.ifi_oerrors; sdl = (struct sockaddr_dl *)(ifm + 1); sdl->sdl_data[sdl->sdl_nlen] = 0; strncpy(rif->if_name, sdl->sdl_data, MIN(sizeof(rif->if_name), sdl->sdl_nlen)); if (rif->status == RIF_NEW) { rif->status = RIF_EXIST; /* the interfaces that we think we can apply reservation * Ethernet is here assuming we use Ethernet switches */ if (rif->if_type == IFT_ETHER || /* huh? */ rif->if_type == IFT_T1 || rif->if_type == IFT_CEPT || rif->if_type == IFT_PPP || rif->if_type == IFT_DS3 || rif->if_type == IFT_FRELAY || rif->if_type == IFT_ATM || rif->if_type == IFT_SONET || rif->if_type == IFT_FRELAYDCE || rif->if_type == IFT_V35 || rif->if_type == IFT_HSSI || rif->if_type == IFT_HIPPI || rif->if_type == IFT_AAL5) rif->state = RESV_ENABLE; else rif->state = RESV_DISABLE; #ifndef DEBUG if (rif->if_type == IFT_LOOP) { rif->state = RESV_ENABLE; rif->link_bw = 1000000; /* 1Mbps */ } #endif /* default settings: * convert from bit/sec to byte/sec */ rif->max_resv_bw = rif->link_bw/8; rif->avail_bw = rif->link_bw/8; } return; } void rif_down(RIF *rif) { /* to speed up the local repair, search for the reservation * flow table here to redirect reservations to new interfaces */ rif_del(rif); return; } /* map routing information to */ void rif_xaddrs(struct rt_addrinfo *data, struct ifa_msghdr *ifam) { struct sockaddr *sa; char *lim, *sa_lim; int i, addrs, len; bzero(data, sizeof(*data)); addrs = data->rti_addrs = ifam->ifam_addrs; lim = (char *)((char *)ifam + ifam->ifam_msglen); sa = (struct sockaddr *)(ifam + 1); i = 0; for (; i < RTAX_MAX && (char *)sa < lim; i++) { if ((addrs & (1 << i)) == 0) continue; len = sa->sa_len; /* Check the sockaddr doesn't go past the end of the buffer. */ if (len) { sa_lim = ((char*)sa) + len; if (sa_lim > (char *)lim) /* equal is ok */ return; } else { /* * We allow the last broken sockaddr * to be replaced by a good null one */ data->rti_info[i] = &sa_zero; return; /* had unknown length */ } data->rti_info[i] = sa; sa = (struct sockaddr *)((char*)(sa) + ROUNDUP(len)); } return; } /* Called when received RTM_NEWADDR */ void rif_change(RIF *rif, struct ifa_msghdr *ifam) { struct rt_addrinfo info; struct sockaddr *ifa; rif_xaddrs(&info, ifam); /* map all addr's */ ifa = (struct sockaddr *)(info.rti_info[RTAX_IFA]); if (!ifa && iff_alive(ifam->ifam_flags)) { msglog("rif_change: no if addr"); rif->state = SICK_RIF; return; } if (ifa->sa_family != AF_INET && iff_alive(ifam->ifam_flags)) { msglog("rif_change: not inet addr"); rif->state = SICK_RIF; return; } rif->int_addr = S_ADDR(ifa); /* interface addr */ if (bad_addr(rif->int_addr) && iff_alive(ifam->ifam_flags)) { msglog("rif_change: bad addr"); rif->state = SICK_RIF; return; } if(ifam->ifam_flags & IFF_LOOPBACK) { rif->int_dstaddr = rif->int_addr; rif->int_mask = HOSTMASK; } else if (ifam->ifam_flags & IFF_POINTOPOINT) { ifa = (struct sockaddr *)(info.rti_info[RTAX_BRD]); if ((!ifa || ifa->sa_family != AF_INET) && iff_alive(ifam->ifam_flags)) { msglog("rif_change: bad dst addr"); rif->state = SICK_RIF; return; } rif->int_dstaddr = S_ADDR(ifa); if (bad_addr(rif->int_dstaddr) && iff_alive(ifam->ifam_flags)) { msglog("rif_change: bad addr"); rif->state = SICK_RIF; return; } rif->int_mask = HOSTMASK; } else { /* such as shared media */ ifa = (struct sockaddr *)(info.rti_info[RTAX_NETMASK]); if (!ifa && iff_alive(ifam->ifam_flags)) { msglog("rif_change: bad mask"); rif->state = SICK_RIF; return; } rif->int_dstaddr = rif->int_addr; rif->int_mask = S_ADDR(ifa); } return; }