int if_managelink(struct dhcpcd_ctx *ctx) { /* route and ifwatchd like a msg buf size of 2048 */ char msg[2048], *p, *e, *cp; ssize_t bytes; struct rt_msghdr *rtm; struct if_announcemsghdr *ifan; struct if_msghdr *ifm; struct ifa_msghdr *ifam; struct sockaddr *sa, *rti_info[RTAX_MAX]; int len; struct sockaddr_dl sdl; struct interface *ifp; #ifdef INET struct rt rt; #endif #ifdef INET6 struct rt6 rt6; struct in6_addr ia6; struct sockaddr_in6 *sin6; int ifa_flags; #endif bytes = read(ctx->link_fd, msg, sizeof(msg)); if (bytes == -1) return -1; if (bytes == 0) return 0; e = msg + bytes; for (p = msg; p < e; p += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)p; // Ignore messages generated by us if (rtm->rtm_pid == getpid()) break; switch(rtm->rtm_type) { #ifdef RTM_IFANNOUNCE case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *)(void *)p; switch(ifan->ifan_what) { case IFAN_ARRIVAL: dhcpcd_handleinterface(ctx, 1, ifan->ifan_name); break; case IFAN_DEPARTURE: dhcpcd_handleinterface(ctx, -1, ifan->ifan_name); break; } break; #endif case RTM_IFINFO: ifm = (struct if_msghdr *)(void *)p; if ((ifp = if_findindex(ctx, ifm->ifm_index)) == NULL) break; switch (ifm->ifm_data.ifi_link_state) { case LINK_STATE_DOWN: len = LINK_DOWN; break; case LINK_STATE_UP: len = LINK_UP; break; default: /* handle_carrier will re-load * the interface flags and check for * IFF_RUNNING as some drivers that * don't handle link state also don't * set IFF_RUNNING when this routing * message is generated. * As such, it is a race ...*/ len = LINK_UNKNOWN; break; } dhcpcd_handlecarrier(ctx, len, (unsigned int)ifm->ifm_flags, ifp->name); break; case RTM_DELETE: if (~rtm->rtm_addrs & (RTA_DST | RTA_GATEWAY | RTA_NETMASK)) break; cp = (char *)(void *)(rtm + 1); sa = (struct sockaddr *)(void *)cp; get_addrs(rtm->rtm_addrs, cp, rti_info); switch (sa->sa_family) { #ifdef INET case AF_INET: memset(&rt, 0, sizeof(rt)); rt.iface = NULL; COPYOUT(rt.dest, rti_info[RTAX_DST]); COPYOUT(rt.net, rti_info[RTAX_NETMASK]); COPYOUT(rt.gate, rti_info[RTAX_GATEWAY]); ipv4_routedeleted(ctx, &rt); break; #endif #ifdef INET6 case AF_INET6: memset(&rt6, 0, sizeof(rt6)); rt6.iface = NULL; COPYOUT6(rt6.dest, rti_info[RTAX_DST]); COPYOUT6(rt6.net, rti_info[RTAX_NETMASK]); COPYOUT6(rt6.gate, rti_info[RTAX_GATEWAY]); ipv6_routedeleted(ctx, &rt6); break; #endif } #ifdef RTM_CHGADDR case RTM_CHGADDR: /* FALLTHROUGH */ #endif case RTM_DELADDR: /* FALLTHROUGH */ case RTM_NEWADDR: ifam = (struct ifa_msghdr *)(void *)p; if ((ifp = if_findindex(ctx, ifam->ifam_index)) == NULL) break; cp = (char *)(void *)(ifam + 1); get_addrs(ifam->ifam_addrs, cp, rti_info); if (rti_info[RTAX_IFA] == NULL) break; switch (rti_info[RTAX_IFA]->sa_family) { case AF_LINK: #ifdef RTM_CHGADDR if (rtm->rtm_type != RTM_CHGADDR) break; #else if (rtm->rtm_type != RTM_NEWADDR) break; #endif memcpy(&sdl, rti_info[RTAX_IFA], rti_info[RTAX_IFA]->sa_len); dhcpcd_handlehwaddr(ctx, ifp->name, (const unsigned char*)CLLADDR(&sdl), sdl.sdl_alen); break; #ifdef INET case AF_INET: case 255: /* FIXME: Why 255? */ COPYOUT(rt.dest, rti_info[RTAX_IFA]); COPYOUT(rt.net, rti_info[RTAX_NETMASK]); COPYOUT(rt.gate, rti_info[RTAX_BRD]); ipv4_handleifa(ctx, rtm->rtm_type, NULL, ifp->name, &rt.dest, &rt.net, &rt.gate); break; #endif #ifdef INET6 case AF_INET6: sin6 = (struct sockaddr_in6*)(void *) rti_info[RTAX_IFA]; ia6 = sin6->sin6_addr; #ifdef __KAME__ if (IN6_IS_ADDR_LINKLOCAL(&ia6)) ia6.s6_addr[2] = ia6.s6_addr[3] = '\0'; #endif if (rtm->rtm_type == RTM_NEWADDR) { ifa_flags = if_addrflags6(&ia6, ifp); if (ifa_flags == -1) break; } else ifa_flags = 0; ipv6_handleifa(ctx, rtm->rtm_type, NULL, ifp->name, &ia6, ifa_flags); break; #endif } break; } } return 0; }
static int link_addr(struct dhcpcd_ctx *ctx, struct interface *ifp, struct nlmsghdr *nlm) { size_t len; struct rtattr *rta; struct ifaddrmsg *ifa; struct priv *priv; #ifdef INET struct in_addr addr, net, brd; #endif #ifdef INET6 struct in6_addr addr6; #endif if (nlm->nlmsg_type != RTM_DELADDR && nlm->nlmsg_type != RTM_NEWADDR) return 0; len = nlm->nlmsg_len - sizeof(*nlm); if (len < sizeof(*ifa)) { errno = EBADMSG; return -1; } /* Ignore messages we sent. */ priv = (struct priv*)ctx->priv; if (nlm->nlmsg_pid == priv->route_pid) return 0; ifa = NLMSG_DATA(nlm); if ((ifp = if_findindex(ctx->ifaces, ifa->ifa_index)) == NULL) { /* We don't know about the interface the address is for * so it's not really an error */ return 1; } rta = (struct rtattr *)IFA_RTA(ifa); len = NLMSG_PAYLOAD(nlm, sizeof(*ifa)); switch (ifa->ifa_family) { #ifdef INET case AF_INET: addr.s_addr = brd.s_addr = INADDR_ANY; inet_cidrtoaddr(ifa->ifa_prefixlen, &net); while (RTA_OK(rta, len)) { switch (rta->rta_type) { case IFA_ADDRESS: if (ifp->flags & IFF_POINTOPOINT) { memcpy(&brd.s_addr, RTA_DATA(rta), sizeof(brd.s_addr)); } break; case IFA_BROADCAST: memcpy(&brd.s_addr, RTA_DATA(rta), sizeof(brd.s_addr)); break; case IFA_LOCAL: memcpy(&addr.s_addr, RTA_DATA(rta), sizeof(addr.s_addr)); break; } rta = RTA_NEXT(rta, len); } ipv4_handleifa(ctx, nlm->nlmsg_type, NULL, ifp->name, &addr, &net, &brd, ifa->ifa_flags, (pid_t)nlm->nlmsg_pid); break; #endif #ifdef INET6 case AF_INET6: memset(&addr6, 0, sizeof(addr6)); while (RTA_OK(rta, len)) { switch (rta->rta_type) { case IFA_ADDRESS: memcpy(&addr6.s6_addr, RTA_DATA(rta), sizeof(addr6.s6_addr)); break; } rta = RTA_NEXT(rta, len); } ipv6_handleifa(ctx, nlm->nlmsg_type, NULL, ifp->name, &addr6, ifa->ifa_prefixlen, ifa->ifa_flags, (pid_t)nlm->nlmsg_pid); break; #endif } return 0; }
static int link_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp, struct nlmsghdr *nlm) { int r; size_t len; struct rtattr *rta, *hwaddr; struct ifinfomsg *ifi; char ifn[IF_NAMESIZE + 1]; r = link_route(ctx, ifp, nlm); if (r != 0) return r; r = link_addr(ctx, ifp, nlm); if (r != 0) return r; #ifdef INET6 r = link_neigh(ctx, ifp, nlm); if (r != 0) return r; #endif if (nlm->nlmsg_type != RTM_NEWLINK && nlm->nlmsg_type != RTM_DELLINK) return 0; len = nlm->nlmsg_len - sizeof(*nlm); if ((size_t)len < sizeof(*ifi)) { errno = EBADMSG; return -1; } ifi = NLMSG_DATA(nlm); if (ifi->ifi_flags & IFF_LOOPBACK) return 0; rta = (void *)((char *)ifi + NLMSG_ALIGN(sizeof(*ifi))); len = NLMSG_PAYLOAD(nlm, sizeof(*ifi)); *ifn = '\0'; hwaddr = NULL; while (RTA_OK(rta, len)) { switch (rta->rta_type) { case IFLA_WIRELESS: /* Ignore wireless messages */ if (nlm->nlmsg_type == RTM_NEWLINK && ifi->ifi_change == 0) return 0; break; case IFLA_IFNAME: strlcpy(ifn, (char *)RTA_DATA(rta), sizeof(ifn)); break; case IFLA_ADDRESS: hwaddr = rta; break; } rta = RTA_NEXT(rta, len); } if (nlm->nlmsg_type == RTM_DELLINK) { /* If are listening to a dev manager, let that remove * the interface rather than the kernel. */ if (dev_listening(ctx) < 1) dhcpcd_handleinterface(ctx, -1, ifn); return 0; } /* Virtual interfaces may not get a valid hardware address * at this point. * To trigger a valid hardware address pickup we need to pretend * that that don't exist until they have one. */ if (ifi->ifi_flags & IFF_MASTER && !hwaddr) { dhcpcd_handleinterface(ctx, -1, ifn); return 0; } /* Check for a new interface */ ifp = if_findindex(ctx->ifaces, (unsigned int)ifi->ifi_index); if (ifp == NULL) { /* If are listening to a dev manager, let that announce * the interface rather than the kernel. */ if (dev_listening(ctx) < 1) dhcpcd_handleinterface(ctx, 1, ifn); return 0; } /* Handle interface being renamed */ if (strcmp(ifp->name, ifn) != 0) { dhcpcd_handleinterface(ctx, -1, ifn); dhcpcd_handleinterface(ctx, 1, ifn); return 0; } /* Re-read hardware address and friends */ if (!(ifi->ifi_flags & IFF_UP) && hwaddr) { uint8_t l; l = l2addr_len(ifi->ifi_type); if (hwaddr->rta_len == RTA_LENGTH(l)) dhcpcd_handlehwaddr(ctx, ifn, RTA_DATA(hwaddr), l); } dhcpcd_handlecarrier(ctx, ifi->ifi_flags & IFF_RUNNING ? LINK_UP : LINK_DOWN, ifi->ifi_flags, ifn); return 0; }
static int if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct nlmsghdr *nlm) { size_t len; struct rtmsg *rtm; struct rtattr *rta; unsigned int ifindex; struct sockaddr *sa; len = nlm->nlmsg_len - sizeof(*nlm); if (len < sizeof(*rtm)) { errno = EBADMSG; return -1; } rtm = (struct rtmsg *)NLMSG_DATA(nlm); if (rtm->rtm_table != RT_TABLE_MAIN) return -1; memset(rt, 0, sizeof(*rt)); if (rtm->rtm_type == RTN_UNREACHABLE) rt->rt_flags |= RTF_REJECT; rta = (struct rtattr *)RTM_RTA(rtm); len = RTM_PAYLOAD(nlm); while (RTA_OK(rta, len)) { sa = NULL; switch (rta->rta_type) { case RTA_DST: sa = &rt->rt_dest; break; case RTA_GATEWAY: sa = &rt->rt_gateway; break; case RTA_PREFSRC: sa = &rt->rt_ifa; break; case RTA_OIF: ifindex = *(unsigned int *)RTA_DATA(rta); rt->rt_ifp = if_findindex(ctx->ifaces, ifindex); break; case RTA_PRIORITY: rt->rt_metric = *(unsigned int *)RTA_DATA(rta); break; case RTA_METRICS: { struct rtattr *r2; size_t l2; l2 = rta->rta_len; r2 = (struct rtattr *)RTA_DATA(rta); while (RTA_OK(r2, l2)) { switch (r2->rta_type) { case RTAX_MTU: rt->rt_mtu = *(unsigned int *)RTA_DATA(r2); break; } r2 = RTA_NEXT(r2, l2); } break; } } if (sa != NULL) { socklen_t salen; sa->sa_family = rtm->rtm_family; salen = sa_addrlen(sa); memcpy((char *)sa + sa_addroffset(sa), RTA_DATA(rta), MIN(salen, RTA_PAYLOAD(rta))); } rta = RTA_NEXT(rta, len); } /* If no RTA_DST set the unspecified address for the family. */ if (rt->rt_dest.sa_family == AF_UNSPEC) rt->rt_dest.sa_family = rtm->rtm_family; rt->rt_netmask.sa_family = rtm->rtm_family; sa_fromprefix(&rt->rt_netmask, rtm->rtm_dst_len); if (sa_is_allones(&rt->rt_netmask)) rt->rt_flags |= RTF_HOST; #if 0 if (rt->rtp_ifp == NULL && rt->src.s_addr != INADDR_ANY) { struct ipv4_addr *ap; /* For some reason the default route comes back with the * loopback interface in RTA_OIF? Lets find it by * preferred source address */ if ((ap = ipv4_findaddr(ctx, &rt->src))) rt->iface = ap->iface; } #endif if (rt->rt_ifp == NULL) { errno = ESRCH; return -1; } return 0; }