From: Roy Marples Date: Mon, 4 Feb 2013 11:03:08 +0000 (+0000) Subject: Move ipv4 specific code out of net.c and into ipv4.c X-Git-Tag: v5.99.6~79 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e88c525f5026816627d4892e8c4595e6becebd91;p=thirdparty%2Fdhcpcd.git Move ipv4 specific code out of net.c and into ipv4.c --- diff --git a/arp.c b/arp.c index 9bf1a597..d61053a4 100644 --- a/arp.c +++ b/arp.c @@ -72,7 +72,7 @@ arp_send(const struct interface *ifp, int op, in_addr_t sip, in_addr_t tip) memcpy(p, &tip, sizeof(tip)); p += sizeof(tip); len = p - arp_buffer; - retval = send_raw_packet(ifp, ETHERTYPE_ARP, arp_buffer, len); + retval = ipv4_sendrawpacket(ifp, ETHERTYPE_ARP, arp_buffer, len); return retval; } @@ -121,7 +121,7 @@ arp_packet(void *arg) state = D_STATE(ifp); state->fail.s_addr = 0; for(;;) { - bytes = get_raw_packet(ifp, ETHERTYPE_ARP, + bytes = ipv4_getrawpacket(ifp, ETHERTYPE_ARP, arp_buffer, sizeof(arp_buffer), NULL); if (bytes == 0 || bytes == -1) return; @@ -210,7 +210,7 @@ arp_announce(void *arg) if (state->new == NULL) return; if (state->arp_fd == -1) { - open_socket(ifp, ETHERTYPE_ARP); + ipv4_opensocket(ifp, ETHERTYPE_ARP); eloop_event_add(state->arp_fd, arp_packet, ifp); } if (++state->claims < ANNOUNCE_NUM) @@ -268,7 +268,7 @@ arp_probe(void *arg) addr.s_addr = state->addr.s_addr; if (state->arp_fd == -1) { - open_socket(ifp, ETHERTYPE_ARP); + ipv4_opensocket(ifp, ETHERTYPE_ARP); eloop_event_add(state->arp_fd, arp_packet, ifp); } if (state->probes == 0) { diff --git a/dhcp.c b/dhcp.c index f217073d..3d4da3b0 100644 --- a/dhcp.c +++ b/dhcp.c @@ -34,9 +34,16 @@ # include #endif +#include +#include +#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */ +#include +#undef __FAVOR_BSD + #include #include #include +#include #include #include #include @@ -199,6 +206,14 @@ static const char *dhcp_params[] = { NULL }; +struct udp_dhcp_packet +{ + struct ip ip; + struct udphdr udp; + struct dhcp_message dhcp; +}; +static const size_t udp_dhcp_len = sizeof(struct udp_dhcp_packet); + static int dhcp_open(struct interface *); void @@ -474,7 +489,7 @@ decode_rfc3442_rt(int dl, const uint8_t *data) while (p < e) { cidr = *p++; if (cidr > 32) { - free_routes(routes); + ipv4_freeroutes(routes); errno = EINVAL; return NULL; } @@ -1143,7 +1158,7 @@ configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp, addr.s_addr = dhcp->yiaddr ? dhcp->yiaddr : dhcp->ciaddr; setvar(&ep, prefix, "ip_address", inet_ntoa(addr)); if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1) { - net.s_addr = get_netmask(addr.s_addr); + net.s_addr = ipv4_getnetmask(addr.s_addr); setvar(&ep, prefix, "subnet_mask", inet_ntoa(net)); } snprintf(cidr, sizeof(cidr), "%d", inet_ntocidr(net)); @@ -1200,7 +1215,7 @@ get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp) else lease->addr.s_addr = dhcp->ciaddr; if (get_option_addr(&lease->net, dhcp, DHO_SUBNETMASK) == -1) - lease->net.s_addr = get_netmask(lease->addr.s_addr); + lease->net.s_addr = ipv4_getnetmask(lease->addr.s_addr); if (get_option_addr(&lease->brd, dhcp, DHO_BROADCAST) == -1) lease->brd.s_addr = lease->addr.s_addr | ~lease->net.s_addr; if (get_option_uint32(&lease->leasetime, dhcp, DHO_LEASETIME) == 0) { @@ -1282,6 +1297,141 @@ dhcp_close(struct interface *ifp) state->interval = 0; } +static int +dhcp_openudp(struct interface *iface) +{ + int s; + struct sockaddr_in sin; + int n; + struct dhcp_state *state; +#ifdef SO_BINDTODEVICE + struct ifreq ifr; + char *p; +#endif + + if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + return -1; + + n = 1; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1) + goto eexit; +#ifdef SO_BINDTODEVICE + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name)); + /* We can only bind to the real device */ + p = strchr(ifr.ifr_name, ':'); + if (p) + *p = '\0'; + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr, + sizeof(ifr)) == -1) + goto eexit; +#endif + /* As we don't use this socket for receiving, set the + * * receive buffer to 1 */ + n = 1; + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1) + goto eexit; + state = D_STATE(iface); + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(DHCP_CLIENT_PORT); + sin.sin_addr.s_addr = state->addr.s_addr; + if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) + goto eexit; + + state->udp_fd = s; + set_cloexec(s); + return 0; + +eexit: + close(s); + return -1; +} + +static ssize_t +dhcp_sendpacket(const struct interface *iface, struct in_addr to, + const uint8_t *data, ssize_t len) +{ + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = to.s_addr; + sin.sin_port = htons(DHCP_SERVER_PORT); + return sendto(D_CSTATE(iface)->udp_fd, data, len, 0, + (struct sockaddr *)&sin, sizeof(sin)); +} + +static uint16_t +checksum(const void *data, uint16_t len) +{ + const uint8_t *addr = data; + uint32_t sum = 0; + + while (len > 1) { + sum += addr[0] * 256 + addr[1]; + addr += 2; + len -= 2; + } + + if (len == 1) + sum += *addr * 256; + + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + + sum = htons(sum); + + return ~sum; +} + +static ssize_t +dhcp_makeudppacket(uint8_t **p, const uint8_t *data, size_t length, + struct in_addr source, struct in_addr dest) +{ + struct udp_dhcp_packet *udpp; + struct ip *ip; + struct udphdr *udp; + + udpp = xzalloc(sizeof(*udpp)); + ip = &udpp->ip; + udp = &udpp->udp; + + /* OK, this is important :) + * We copy the data to our packet and then create a small part of the + * ip structure and an invalid ip_len (basically udp length). + * We then fill the udp structure and put the checksum + * of the whole packet into the udp checksum. + * Finally we complete the ip structure and ip checksum. + * If we don't do the ordering like so then the udp checksum will be + * broken, so find another way of doing it! */ + + memcpy(&udpp->dhcp, data, length); + + ip->ip_p = IPPROTO_UDP; + ip->ip_src.s_addr = source.s_addr; + if (dest.s_addr == 0) + ip->ip_dst.s_addr = INADDR_BROADCAST; + else + ip->ip_dst.s_addr = dest.s_addr; + + udp->uh_sport = htons(DHCP_CLIENT_PORT); + udp->uh_dport = htons(DHCP_SERVER_PORT); + udp->uh_ulen = htons(sizeof(*udp) + length); + ip->ip_len = udp->uh_ulen; + udp->uh_sum = checksum(udpp, sizeof(*udpp)); + + ip->ip_v = IPVERSION; + ip->ip_hl = sizeof(*ip) >> 2; + ip->ip_id = arc4random() & UINT16_MAX; + ip->ip_ttl = IPDEFTTL; + ip->ip_len = htons(sizeof(*ip) + sizeof(*udp) + length); + ip->ip_sum = checksum(ip, sizeof(*ip)); + + *p = (uint8_t *)udpp; + return sizeof(*ip) + sizeof(*udp) + length; +} + static void send_message(struct interface *iface, int type, void (*callback)(void *)) @@ -1341,14 +1491,14 @@ send_message(struct interface *iface, int type, else to.s_addr = 0; if (to.s_addr && to.s_addr != INADDR_BROADCAST) { - r = send_packet(iface, to, (uint8_t *)dhcp, len); + r = dhcp_sendpacket(iface, to, (uint8_t *)dhcp, len); if (r == -1) { - syslog(LOG_ERR, "%s: send_packet: %m", iface->name); + syslog(LOG_ERR, "%s: dhcp_sendpacket: %m", iface->name); dhcp_close(iface); } } else { - len = make_udp_packet(&udp, (uint8_t *)dhcp, len, from, to); - r = send_raw_packet(iface, ETHERTYPE_IP, udp, len); + len = dhcp_makeudppacket(&udp, (uint8_t *)dhcp, len, from, to); + r = ipv4_sendrawpacket(iface, ETHERTYPE_IP, udp, len); free(udp); /* If we failed to send a raw packet this normally means * we don't have the ability to work beneath the IP layer @@ -1657,7 +1807,6 @@ dhcp_bind(void *arg) } } - static void dhcp_timeout(void *arg) { @@ -1699,7 +1848,7 @@ handle_3rdparty(struct interface *ifp) if (ifo->req_addr.s_addr != INADDR_ANY) return 0; - if (get_address(ifp->name, &addr, &net, &dst) == 1) + if (ipv4_getaddress(ifp->name, &addr, &net, &dst) == 1) ipv4_handleifa(RTM_NEWADDR, ifp->name, &addr, &net, &dst); else { syslog(LOG_INFO, @@ -1812,7 +1961,6 @@ dhcp_reboot(struct interface *ifp, int oldopts) dhcp_request(ifp); } - void dhcp_drop(struct interface *iface, const char *reason) { @@ -2042,7 +2190,7 @@ dhcp_handle(struct interface *iface, struct dhcp_message **dhcpp, /* If the interface already has the address configured * then we can't ARP for duplicate detection. */ addr.s_addr = state->offer->yiaddr; - if (has_address(iface->name, &addr, NULL) != 1) { + if (ipv4_hasaddress(iface->name, &addr, NULL) != 1) { state->claims = 0; state->probes = 0; state->conflicts = 0; @@ -2055,6 +2203,67 @@ dhcp_handle(struct interface *iface, struct dhcp_message **dhcpp, dhcp_bind(iface); } +static ssize_t +get_udp_data(const uint8_t **data, const uint8_t *udp) +{ + struct udp_dhcp_packet p; + + memcpy(&p, udp, sizeof(p)); + *data = udp + offsetof(struct udp_dhcp_packet, dhcp); + return ntohs(p.ip.ip_len) - sizeof(p.ip) - sizeof(p.udp); +} + +static int +valid_udp_packet(const uint8_t *data, size_t data_len, struct in_addr *from, + int noudpcsum) +{ + struct udp_dhcp_packet p; + uint16_t bytes, udpsum; + + if (data_len < sizeof(p.ip)) { + if (from) + from->s_addr = INADDR_ANY; + errno = EINVAL; + return -1; + } + memcpy(&p, data, MIN(data_len, sizeof(p))); + if (from) + from->s_addr = p.ip.ip_src.s_addr; + if (data_len > sizeof(p)) { + errno = EINVAL; + return -1; + } + if (checksum(&p.ip, sizeof(p.ip)) != 0) { + errno = EINVAL; + return -1; + } + + bytes = ntohs(p.ip.ip_len); + if (data_len < bytes) { + errno = EINVAL; + return -1; + } + + if (noudpcsum == 0) { + udpsum = p.udp.uh_sum; + p.udp.uh_sum = 0; + p.ip.ip_hl = 0; + p.ip.ip_v = 0; + p.ip.ip_tos = 0; + p.ip.ip_len = p.udp.uh_ulen; + p.ip.ip_id = 0; + p.ip.ip_off = 0; + p.ip.ip_ttl = 0; + p.ip.ip_sum = 0; + if (udpsum && checksum(&p, bytes) != udpsum) { + errno = EINVAL; + return -1; + } + } + + return 0; +} + static void dhcp_handlepacket(void *arg) { @@ -2072,7 +2281,7 @@ dhcp_handlepacket(void *arg) if (packet == NULL) packet = xmalloc(udp_dhcp_len); for(;;) { - bytes = get_raw_packet(iface, ETHERTYPE_IP, + bytes = ipv4_getrawpacket(iface, ETHERTYPE_IP, packet, udp_dhcp_len, &partialcsum); if (bytes == 0 || bytes == -1) break; @@ -2151,7 +2360,7 @@ dhcp_open(struct interface *ifp) state = D_STATE(ifp); if (state->raw_fd == -1) { - if ((r = open_socket(ifp, ETHERTYPE_IP)) == -1) + if ((r = ipv4_opensocket(ifp, ETHERTYPE_IP)) == -1) syslog(LOG_ERR, "%s: %s: %m", __func__, ifp->name); else eloop_event_add(state->raw_fd, dhcp_handlepacket, ifp); @@ -2162,8 +2371,8 @@ dhcp_open(struct interface *ifp) (state->new->cookie == htonl(MAGIC_COOKIE) || ifp->options->options & DHCPCD_INFORM)) { - if (open_udp_socket(ifp) == -1 && errno != EADDRINUSE) { - syslog(LOG_ERR, "%s: open_udp_socket: %m", ifp->name); + if (dhcp_openudp(ifp) == -1 && errno != EADDRINUSE) { + syslog(LOG_ERR, "%s: dhcp_openudp: %m", ifp->name); r = -1; } } diff --git a/dhcpcd.c b/dhcpcd.c index 9f8779e9..6f9e32a4 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -1091,8 +1091,8 @@ main(int argc, char **argv) syslog(LOG_ERR, "control_start: %m"); } - if (init_sockets() == -1) { - syslog(LOG_ERR, "init_socket: %m"); + if (open_sockets() == -1) { + syslog(LOG_ERR, "open_sockets: %m"); exit(EXIT_FAILURE); } if (if_options->options & DHCPCD_LINK) { diff --git a/if-linux.c b/if-linux.c index 34248d0c..606a5aab 100644 --- a/if-linux.c +++ b/if-linux.c @@ -126,7 +126,7 @@ _open_link_socket(struct sockaddr_nl *nl) } int -init_sockets(void) +open_sockets(void) { if ((socket_afnet = socket(AF_INET, SOCK_DGRAM, 0)) == -1) return -1; diff --git a/if-options.c b/if-options.c index 5d319350..89f0b051 100644 --- a/if-options.c +++ b/if-options.c @@ -45,7 +45,7 @@ #include "dhcp.h" #include "dhcp6.h" #include "if-options.h" -#include "net.h" +#include "ipv4.h" #include "platform.h" unsigned long long options = 0; @@ -334,7 +334,7 @@ parse_addr(struct in_addr *addr, struct in_addr *net, const char *arg) if (p != NULL) *--p = '/'; else if (net != NULL) - net->s_addr = get_netmask(addr->s_addr); + net->s_addr = ipv4_getnetmask(addr->s_addr); return 0; } @@ -974,7 +974,7 @@ free_options(struct if_options *ifo) free(ifo->config[i++]); free(ifo->config); } - free_routes(ifo->routes); + ipv4_freeroutes(ifo->routes); free(ifo->arping); free(ifo->blacklist); free(ifo->fallback); diff --git a/ipv4.c b/ipv4.c index e2170acf..eee8ec4a 100644 --- a/ipv4.c +++ b/ipv4.c @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -49,8 +50,122 @@ #include "net.h" #include "script.h" +int socket_afnet = -1; + static struct rt *routes; +int +inet_ntocidr(struct in_addr address) +{ + int cidr = 0; + uint32_t mask = htonl(address.s_addr); + + while (mask) { + cidr++; + mask <<= 1; + } + return cidr; +} + +int +inet_cidrtoaddr(int cidr, struct in_addr *addr) +{ + int ocets; + + if (cidr < 1 || cidr > 32) { + errno = EINVAL; + return -1; + } + ocets = (cidr + 7) / 8; + + addr->s_addr = 0; + if (ocets > 0) { + memset(&addr->s_addr, 255, (size_t)ocets - 1); + memset((unsigned char *)&addr->s_addr + (ocets - 1), + (256 - (1 << (32 - cidr) % 8)), 1); + } + + return 0; +} + +uint32_t +ipv4_getnetmask(uint32_t addr) +{ + uint32_t dst; + + if (addr == 0) + return 0; + + dst = htonl(addr); + if (IN_CLASSA(dst)) + return ntohl(IN_CLASSA_NET); + if (IN_CLASSB(dst)) + return ntohl(IN_CLASSB_NET); + if (IN_CLASSC(dst)) + return ntohl(IN_CLASSC_NET); + + return 0; +} + +int +ipv4_doaddress(const char *ifname, + struct in_addr *addr, struct in_addr *net, struct in_addr *dst, int act) +{ + struct ifaddrs *ifaddrs, *ifa; + const struct sockaddr_in *a, *n, *d; + int retval; + + if (getifaddrs(&ifaddrs) == -1) + return -1; + + retval = 0; + for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL || + ifa->ifa_addr->sa_family != AF_INET || + strcmp(ifa->ifa_name, ifname) != 0) + continue; + a = (const struct sockaddr_in *)(void *)ifa->ifa_addr; + n = (const struct sockaddr_in *)(void *)ifa->ifa_netmask; + if (ifa->ifa_flags & IFF_POINTOPOINT) + d = (const struct sockaddr_in *)(void *) + ifa->ifa_dstaddr; + else + d = NULL; + if (act == 1) { + addr->s_addr = a->sin_addr.s_addr; + net->s_addr = n->sin_addr.s_addr; + if (dst) { + if (ifa->ifa_flags & IFF_POINTOPOINT) + dst->s_addr = d->sin_addr.s_addr; + else + dst->s_addr = INADDR_ANY; + } + retval = 1; + break; + } + if (addr->s_addr == a->sin_addr.s_addr && + (net == NULL || net->s_addr == n->sin_addr.s_addr)) + { + retval = 1; + break; + } + } + freeifaddrs(ifaddrs); + return retval; +} + +void +ipv4_freeroutes(struct rt *rts) +{ + struct rt *r; + + while (rts) { + r = rts->next; + free(rts); + rts = r; + } +} + static struct rt * find_route(struct rt *rts, const struct rt *r, struct rt **lrt, const struct rt *srt) @@ -127,7 +242,7 @@ n_route(struct rt *rt) return -1; desc_route("adding", rt); - if (!add_route(rt)) + if (!ipv4_addroute(rt)) return 0; if (errno == EEXIST) { s = D_CSTATE(rt->iface); @@ -139,7 +254,7 @@ n_route(struct rt *rt) else return -1; } - syslog(LOG_ERR, "%s: add_route: %m", rt->iface->name); + syslog(LOG_ERR, "%s: ipv4_addroute: %m", rt->iface->name); return -1; } @@ -156,10 +271,10 @@ c_route(struct rt *ort, struct rt *nrt) /* We delete and add the route so that we can change metric. * This also has the nice side effect of flushing ARP entries so * we don't have to do that manually. */ - del_route(ort); - if (!add_route(nrt)) + ipv4_deleteroute(ort); + if (!ipv4_addroute(nrt)) return 0; - syslog(LOG_ERR, "%s: add_route: %m", nrt->iface->name); + syslog(LOG_ERR, "%s: ipv4_addroute: %m", nrt->iface->name); return -1; } @@ -169,9 +284,9 @@ d_route(struct rt *rt) int retval; desc_route("deleting", rt); - retval = del_route(rt); + retval = ipv4_deleteroute(rt); if (retval != 0 && errno != ENOENT && errno != ESRCH) - syslog(LOG_ERR,"%s: del_route: %m", rt->iface->name); + syslog(LOG_ERR,"%s: ipv4_deleteroute: %m", rt->iface->name); return retval; } @@ -187,7 +302,7 @@ get_subnet_route(struct dhcp_message *dhcp) addr = dhcp->ciaddr; /* Ensure we have all the needed values */ if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1) - net.s_addr = get_netmask(addr); + net.s_addr = ipv4_getnetmask(addr); if (net.s_addr == INADDR_BROADCAST || net.s_addr == INADDR_ANY) return NULL; rt = malloc(sizeof(*rt)); @@ -382,7 +497,7 @@ ipv4_buildroutes(void) nrs = rt; rt = lrt; /* When we loop this makes lrt correct */ } - free_routes(dnr); + ipv4_freeroutes(dnr); } /* Remove old routes we used to manage */ @@ -391,7 +506,7 @@ ipv4_buildroutes(void) d_route(rt); } - free_routes(routes); + ipv4_freeroutes(routes); routes = nrs; } @@ -409,7 +524,7 @@ delete_address(struct interface *iface) return 0; syslog(LOG_DEBUG, "%s: deleting IP address %s/%d", iface->name, inet_ntoa(state->addr), inet_ntocidr(state->net)); - retval = del_address(iface, &state->addr, &state->net); + retval = ipv4_deleteaddress(iface, &state->addr, &state->net); if (retval == -1 && errno != EADDRNOTAVAIL) syslog(LOG_ERR, "del_address: %m"); state->addr.s_addr = 0; @@ -448,16 +563,16 @@ ipv4_applyaddr(void *arg) /* This also changes netmask */ if (!(ifo->options & DHCPCD_INFORM) || - !has_address(ifp->name, &lease->addr, &lease->net)) + !ipv4_hasaddress(ifp->name, &lease->addr, &lease->net)) { syslog(LOG_DEBUG, "%s: adding IP address %s/%d", ifp->name, inet_ntoa(lease->addr), inet_ntocidr(lease->net)); - if (add_address(ifp, + if (ipv4_addaddress(ifp, &lease->addr, &lease->net, &lease->brd) == -1 && errno != EEXIST) { - syslog(LOG_ERR, "%s: add_address: %m", __func__); + syslog(LOG_ERR, "%s: ipv4_addaddress: %m", __func__); return; } } @@ -477,7 +592,7 @@ ipv4_applyaddr(void *arg) rt->iface = ifp; rt->metric = 0; if (!find_route(routes, rt, NULL, NULL)) - del_route(rt); + ipv4_deleteroute(rt); free(rt); } diff --git a/ipv4.h b/ipv4.h index a13f4478..574222fb 100644 --- a/ipv4.h +++ b/ipv4.h @@ -28,7 +28,21 @@ #ifndef IPV4_H #define IPV4_H -#include "net.h" +#include "dhcpcd.h" + +struct rt { + struct in_addr dest; + struct in_addr net; + struct in_addr gate; + const struct interface *iface; + int metric; + struct in_addr src; + struct rt *next; +}; + +int inet_ntocidr(struct in_addr); +int inet_cidrtoaddr(int, struct in_addr *); +uint32_t ipv4_getnetmask(uint32_t); void ipv4_buildroutes(void); void ipv4_applyaddr(void *); @@ -36,4 +50,31 @@ int ipv4_routedeleted(const struct rt *); void ipv4_handleifa(int, const char *, struct in_addr *, struct in_addr *, struct in_addr *); + +int ipv4_doaddress(const char *, + struct in_addr *, struct in_addr *, struct in_addr *, int); +int if_address(const struct interface *, + const struct in_addr *, const struct in_addr *, + const struct in_addr *, int); +#define ipv4_addaddress(iface, addr, net, brd) \ + if_address(iface, addr, net, brd, 1) +#define ipv4_deleteaddress(iface, addr, net) \ + if_address(iface, addr, net, NULL, -1) +#define ipv4_hasaddress(iface, addr, net) \ + ipv4_doaddress(iface, addr, net, NULL, 0) +#define ipv4_getaddress(iface, addr, net, dst) \ + ipv4_doaddress(iface, addr, net, dst, 1) + +int if_route(const struct rt *rt, int); +#define ipv4_addroute(rt) if_route(rt, 1) +#define ipv4_changeroute(rt) if_route(rt, 0) +#define ipv4_deleteroute(rt) if_route(rt, -1) +#define del_src_route(rt) i_route(rt, -2); +void ipv4_freeroutes(struct rt *); + +int ipv4_opensocket(struct interface *, int); +ssize_t ipv4_sendrawpacket(const struct interface *, + int, const void *, ssize_t); +ssize_t ipv4_getrawpacket(struct interface *, int, void *, ssize_t, int *); + #endif diff --git a/lpf.c b/lpf.c index 4c98ade9..407ef4a8 100644 --- a/lpf.c +++ b/lpf.c @@ -54,7 +54,7 @@ #include "config.h" #include "common.h" #include "dhcp.h" -#include "net.h" +#include "ipv4.h" #include "bpf-filter.h" /* Broadcast address for IPoIB */ @@ -65,7 +65,7 @@ static const uint8_t ipv4_bcast_addr[] = { }; int -open_socket(struct interface *ifp, int protocol) +ipv4_opensocket(struct interface *ifp, int protocol) { int s; union sockunion { @@ -128,7 +128,7 @@ eexit: } ssize_t -send_raw_packet(const struct interface *ifp, int protocol, +ipv4_sendrawpacket(const struct interface *ifp, int protocol, const void *data, ssize_t len) { const struct dhcp_state *state; @@ -160,7 +160,7 @@ send_raw_packet(const struct interface *ifp, int protocol, } ssize_t -get_raw_packet(struct interface *ifp, int protocol, +ipv4_getrawpacket(struct interface *ifp, int protocol, void *data, ssize_t len, int *partialcsum) { struct iovec iov = { diff --git a/net.c b/net.c index 11662a2b..0eed0699 100644 --- a/net.c +++ b/net.c @@ -27,23 +27,14 @@ #include #include -#include #include -#include -#include #include #include #ifdef AF_LINK # include # include #endif -#include -#include -#include -#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */ -#include -#undef __FAVOR_BSD #ifdef AF_PACKET # include #endif @@ -69,65 +60,9 @@ #include "if-options.h" #include "ipv6rs.h" #include "net.h" -#include "signals.h" static char hwaddr_buffer[(HWADDR_LEN * 3) + 1 + 1024]; -int socket_afnet = -1; - -int -inet_ntocidr(struct in_addr address) -{ - int cidr = 0; - uint32_t mask = htonl(address.s_addr); - - while (mask) { - cidr++; - mask <<= 1; - } - return cidr; -} - -int -inet_cidrtoaddr(int cidr, struct in_addr *addr) -{ - int ocets; - - if (cidr < 1 || cidr > 32) { - errno = EINVAL; - return -1; - } - ocets = (cidr + 7) / 8; - - addr->s_addr = 0; - if (ocets > 0) { - memset(&addr->s_addr, 255, (size_t)ocets - 1); - memset((unsigned char *)&addr->s_addr + (ocets - 1), - (256 - (1 << (32 - cidr) % 8)), 1); - } - - return 0; -} - -uint32_t -get_netmask(uint32_t addr) -{ - uint32_t dst; - - if (addr == 0) - return 0; - - dst = htonl(addr); - if (IN_CLASSA(dst)) - return ntohl(IN_CLASSA_NET); - if (IN_CLASSB(dst)) - return ntohl(IN_CLASSB_NET); - if (IN_CLASSC(dst)) - return ntohl(IN_CLASSC_NET); - - return 0; -} - char * hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen) { @@ -485,53 +420,6 @@ discover_interfaces(int argc, char * const *argv) return ifs; } -int -do_address(const char *ifname, - struct in_addr *addr, struct in_addr *net, struct in_addr *dst, int act) -{ - struct ifaddrs *ifaddrs, *ifa; - const struct sockaddr_in *a, *n, *d; - int retval; - - if (getifaddrs(&ifaddrs) == -1) - return -1; - - retval = 0; - for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL || - ifa->ifa_addr->sa_family != AF_INET || - strcmp(ifa->ifa_name, ifname) != 0) - continue; - a = (const struct sockaddr_in *)(void *)ifa->ifa_addr; - n = (const struct sockaddr_in *)(void *)ifa->ifa_netmask; - if (ifa->ifa_flags & IFF_POINTOPOINT) - d = (const struct sockaddr_in *)(void *) - ifa->ifa_dstaddr; - else - d = NULL; - if (act == 1) { - addr->s_addr = a->sin_addr.s_addr; - net->s_addr = n->sin_addr.s_addr; - if (dst) { - if (ifa->ifa_flags & IFF_POINTOPOINT) - dst->s_addr = d->sin_addr.s_addr; - else - dst->s_addr = INADDR_ANY; - } - retval = 1; - break; - } - if (addr->s_addr == a->sin_addr.s_addr && - (net == NULL || net->s_addr == n->sin_addr.s_addr)) - { - retval = 1; - break; - } - } - freeifaddrs(ifaddrs); - return retval; -} - int do_mtu(const char *ifname, short int mtu) { @@ -546,221 +434,3 @@ do_mtu(const char *ifname, short int mtu) return -1; return ifr.ifr_mtu; } - -void -free_routes(struct rt *routes) -{ - struct rt *r; - - while (routes) { - r = routes->next; - free(routes); - routes = r; - } -} - -int -open_udp_socket(struct interface *iface) -{ - int s; - struct sockaddr_in sin; - int n; - struct dhcp_state *state; -#ifdef SO_BINDTODEVICE - struct ifreq ifr; - char *p; -#endif - - if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) - return -1; - - n = 1; - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1) - goto eexit; -#ifdef SO_BINDTODEVICE - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name)); - /* We can only bind to the real device */ - p = strchr(ifr.ifr_name, ':'); - if (p) - *p = '\0'; - if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr, - sizeof(ifr)) == -1) - goto eexit; -#endif - /* As we don't use this socket for receiving, set the - * receive buffer to 1 */ - n = 1; - if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1) - goto eexit; - state = D_STATE(iface); - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(DHCP_CLIENT_PORT); - sin.sin_addr.s_addr = state->addr.s_addr; - if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) - goto eexit; - - state->udp_fd = s; - set_cloexec(s); - return 0; - -eexit: - close(s); - return -1; -} - -ssize_t -send_packet(const struct interface *iface, struct in_addr to, - const uint8_t *data, ssize_t len) -{ - struct sockaddr_in sin; - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = to.s_addr; - sin.sin_port = htons(DHCP_SERVER_PORT); - return sendto(D_CSTATE(iface)->udp_fd, data, len, 0, - (struct sockaddr *)&sin, sizeof(sin)); -} - -struct udp_dhcp_packet -{ - struct ip ip; - struct udphdr udp; - struct dhcp_message dhcp; -}; -const size_t udp_dhcp_len = sizeof(struct udp_dhcp_packet); - -static uint16_t -checksum(const void *data, uint16_t len) -{ - const uint8_t *addr = data; - uint32_t sum = 0; - - while (len > 1) { - sum += addr[0] * 256 + addr[1]; - addr += 2; - len -= 2; - } - - if (len == 1) - sum += *addr * 256; - - sum = (sum >> 16) + (sum & 0xffff); - sum += (sum >> 16); - - sum = htons(sum); - - return ~sum; -} - -ssize_t -make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length, - struct in_addr source, struct in_addr dest) -{ - struct udp_dhcp_packet *udpp; - struct ip *ip; - struct udphdr *udp; - - udpp = xzalloc(sizeof(*udpp)); - ip = &udpp->ip; - udp = &udpp->udp; - - /* OK, this is important :) - * We copy the data to our packet and then create a small part of the - * ip structure and an invalid ip_len (basically udp length). - * We then fill the udp structure and put the checksum - * of the whole packet into the udp checksum. - * Finally we complete the ip structure and ip checksum. - * If we don't do the ordering like so then the udp checksum will be - * broken, so find another way of doing it! */ - - memcpy(&udpp->dhcp, data, length); - - ip->ip_p = IPPROTO_UDP; - ip->ip_src.s_addr = source.s_addr; - if (dest.s_addr == 0) - ip->ip_dst.s_addr = INADDR_BROADCAST; - else - ip->ip_dst.s_addr = dest.s_addr; - - udp->uh_sport = htons(DHCP_CLIENT_PORT); - udp->uh_dport = htons(DHCP_SERVER_PORT); - udp->uh_ulen = htons(sizeof(*udp) + length); - ip->ip_len = udp->uh_ulen; - udp->uh_sum = checksum(udpp, sizeof(*udpp)); - - ip->ip_v = IPVERSION; - ip->ip_hl = sizeof(*ip) >> 2; - ip->ip_id = arc4random() & UINT16_MAX; - ip->ip_ttl = IPDEFTTL; - ip->ip_len = htons(sizeof(*ip) + sizeof(*udp) + length); - ip->ip_sum = checksum(ip, sizeof(*ip)); - - *packet = (uint8_t *)udpp; - return sizeof(*ip) + sizeof(*udp) + length; -} - -ssize_t -get_udp_data(const uint8_t **data, const uint8_t *udp) -{ - struct udp_dhcp_packet packet; - - memcpy(&packet, udp, sizeof(packet)); - *data = udp + offsetof(struct udp_dhcp_packet, dhcp); - return ntohs(packet.ip.ip_len) - - sizeof(packet.ip) - - sizeof(packet.udp); -} - -int -valid_udp_packet(const uint8_t *data, size_t data_len, struct in_addr *from, - int noudpcsum) -{ - struct udp_dhcp_packet packet; - uint16_t bytes, udpsum; - - if (data_len < sizeof(packet.ip)) { - if (from) - from->s_addr = INADDR_ANY; - errno = EINVAL; - return -1; - } - memcpy(&packet, data, MIN(data_len, sizeof(packet))); - if (from) - from->s_addr = packet.ip.ip_src.s_addr; - if (data_len > sizeof(packet)) { - errno = EINVAL; - return -1; - } - if (checksum(&packet.ip, sizeof(packet.ip)) != 0) { - errno = EINVAL; - return -1; - } - - bytes = ntohs(packet.ip.ip_len); - if (data_len < bytes) { - errno = EINVAL; - return -1; - } - - if (noudpcsum == 0) { - udpsum = packet.udp.uh_sum; - packet.udp.uh_sum = 0; - packet.ip.ip_hl = 0; - packet.ip.ip_v = 0; - packet.ip.ip_tos = 0; - packet.ip.ip_len = packet.udp.uh_ulen; - packet.ip.ip_id = 0; - packet.ip.ip_off = 0; - packet.ip.ip_ttl = 0; - packet.ip.ip_sum = 0; - if (udpsum && checksum(&packet, bytes) != udpsum) { - errno = EINVAL; - return -1; - } - } - - return 0; -} diff --git a/net.h b/net.h index 0d1475a0..cf52e7da 100644 --- a/net.h +++ b/net.h @@ -89,19 +89,10 @@ # define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR) #endif -struct rt { - struct in_addr dest; - struct in_addr net; - struct in_addr gate; - const struct interface *iface; - int metric; - struct in_addr src; - struct rt *next; -}; - extern int socket_afnet; -uint32_t get_netmask(uint32_t); +int open_sockets(void); + char *hwaddr_ntoa(const unsigned char *, size_t); size_t hwaddr_aton(unsigned char *, const char *); @@ -112,34 +103,10 @@ int do_mtu(const char *, short int); #define get_mtu(iface) do_mtu(iface, 0) #define set_mtu(iface, mtu) do_mtu(iface, mtu) -int inet_ntocidr(struct in_addr); -int inet_cidrtoaddr(int, struct in_addr *); - int up_interface(struct interface *); int if_conf(struct interface *); int if_init(struct interface *); -int do_address(const char *, - struct in_addr *, struct in_addr *, struct in_addr *, int); -int if_address(const struct interface *, - const struct in_addr *, const struct in_addr *, - const struct in_addr *, int); -#define add_address(iface, addr, net, brd) \ - if_address(iface, addr, net, brd, 1) -#define del_address(iface, addr, net) \ - if_address(iface, addr, net, NULL, -1) -#define has_address(iface, addr, net) \ - do_address(iface, addr, net, NULL, 0) -#define get_address(iface, addr, net, dst) \ - do_address(iface, addr, net, dst, 1) - -int if_route(const struct rt *rt, int); -#define add_route(rt) if_route(rt, 1) -#define change_route(rt) if_route(rt, 0) -#define del_route(rt) if_route(rt, -1) -#define del_src_route(rt) if_route(rt, -2); -void free_routes(struct rt *); - int if_address6(const struct interface *, const struct ipv6_addr *, int); #define add_address6(ifp, a) if_address6(ifp, a, 1) #define del_address6(ifp, a) if_address6(ifp, a, -1) @@ -150,21 +117,6 @@ int if_route6(const struct rt6 *rt, int); #define del_route6(rt) if_route6(rt, -1) #define del_src_route6(rt) if_route6(rt, -2); -int open_udp_socket(struct interface *); -extern const size_t udp_dhcp_len; -ssize_t make_udp_packet(uint8_t **, const uint8_t *, size_t, - struct in_addr, struct in_addr); -ssize_t get_udp_data(const uint8_t **, const uint8_t *); -int valid_udp_packet(const uint8_t *, size_t, struct in_addr *, int); - -int open_socket(struct interface *, int); -ssize_t send_packet(const struct interface *, struct in_addr, - const uint8_t *, ssize_t); -ssize_t send_raw_packet(const struct interface *, int, - const void *, ssize_t); -ssize_t get_raw_packet(struct interface *, int, void *, ssize_t, int *); - -int init_sockets(void); int open_link_socket(void); int manage_link(int); int carrier_status(struct interface *);