From: Roy Marples Date: Thu, 24 Mar 2011 17:59:20 +0000 (+0000) Subject: When the dhcp server is running in dom0 and the client in domU, X-Git-Tag: v5.2.12~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=37eea8615aec87bf32f61a1dc001a5c964e9e217;p=thirdparty%2Fdhcpcd.git When the dhcp server is running in dom0 and the client in domU, packets (that are not explicitely checksumed in the userspace) sent to the another domain have partial UDP checksums (offload) only, but are marked as such. This patch reads and checks the mark to decide whether to verify the UDP checksum or not. Based on the ISC dhcp patch by David Cantrell. Thanks to Marius Tomaschewski. --- diff --git a/arp.c b/arp.c index 89d63fe8..907ac113 100644 --- a/arp.c +++ b/arp.c @@ -1,6 +1,6 @@ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2008 Roy Marples + * Copyright (c) 2006-2011 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -119,7 +119,7 @@ handle_arp_packet(void *arg) state->fail.s_addr = 0; for(;;) { bytes = get_raw_packet(iface, ETHERTYPE_ARP, - arp_buffer, sizeof(arp_buffer)); + arp_buffer, sizeof(arp_buffer), NULL); if (bytes == 0 || bytes == -1) return; /* We must have a full ARP header */ diff --git a/bpf.c b/bpf.c index beda1ba0..1c30c762 100644 --- a/bpf.c +++ b/bpf.c @@ -1,6 +1,6 @@ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2008 Roy Marples + * Copyright (c) 2006-2011 Roy Marples * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -160,7 +160,7 @@ send_raw_packet(const struct interface *iface, int protocol, * So we pass the buffer in the API so we can loop on >1 packet. */ ssize_t get_raw_packet(struct interface *iface, int protocol, - void *data, ssize_t len) + void *data, ssize_t len, int *partialcsum) { int fd = -1; struct bpf_hdr packet; @@ -172,6 +172,9 @@ get_raw_packet(struct interface *iface, int protocol, else fd = iface->raw_fd; + if (partialcsum != NULL) + *partialcsum = 0; /* Not supported on BSD */ + for (;;) { if (iface->buffer_len == 0) { bytes = read(fd, iface->buffer, iface->buffer_size); diff --git a/dhcpcd.c b/dhcpcd.c index ade5e62b..6e9305fd 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -641,7 +641,7 @@ handle_dhcp_packet(void *arg) const uint8_t *pp; ssize_t bytes; struct in_addr from; - int i; + int i, partialcsum = 0; /* We loop through until our buffer is empty. * The benefit is that if we get >1 DHCP packet in our buffer and @@ -649,10 +649,10 @@ handle_dhcp_packet(void *arg) packet = xmalloc(udp_dhcp_len); for(;;) { bytes = get_raw_packet(iface, ETHERTYPE_IP, - packet, udp_dhcp_len); + packet, udp_dhcp_len, &partialcsum); if (bytes == 0 || bytes == -1) break; - if (valid_udp_packet(packet, bytes, &from) == -1) { + if (valid_udp_packet(packet, bytes, &from, partialcsum) == -1) { syslog(LOG_ERR, "%s: invalid UDP packet from %s", iface->name, inet_ntoa(from)); continue; diff --git a/lpf.c b/lpf.c index 2907d908..853d0a32 100644 --- a/lpf.c +++ b/lpf.c @@ -1,6 +1,6 @@ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2009 Roy Marples + * Copyright (c) 2006-2011 Roy Marples * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,7 +36,7 @@ #ifdef __linux__ # include /* needed for 2.4 kernels for the below header */ # include -# include +# include # define bpf_insn sock_filter # define BPF_SKIPTYPE # define BPF_ETHCOOK -ETH_HLEN @@ -76,6 +76,9 @@ open_socket(struct interface *iface, int protocol) } su; struct sock_fprog pf; int *fd; +#ifdef PACKET_AUXDATA + int n; +#endif if ((s = socket(PF_PACKET, SOCK_DGRAM, htons(protocol))) == -1) return -1; @@ -98,6 +101,13 @@ open_socket(struct interface *iface, int protocol) } if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf)) != 0) goto eexit; +#ifdef PACKET_AUXDATA + n = 1; + if (setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &n, sizeof(n)) != 0) { + if (errno != ENOPROTOOPT) + goto eexit; + } +#endif if (set_cloexec(s) == -1) goto eexit; if (set_nonblock(s) == -1) @@ -152,17 +162,53 @@ send_raw_packet(const struct interface *iface, int protocol, } ssize_t -get_raw_packet(struct interface *iface, int protocol, void *data, ssize_t len) +get_raw_packet(struct interface *iface, int protocol, + void *data, ssize_t len, int *partialcsum) { + struct iovec iov = { + .iov_base = data, + .iov_len = len, + }; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + }; +#ifdef PACKET_AUXDATA + unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))]; + struct cmsghdr *cmsg; + struct tpacket_auxdata *aux; +#endif + ssize_t bytes; int fd = -1; +#ifdef PACKET_AUXDATA + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); +#endif + if (protocol == ETHERTYPE_ARP) fd = iface->arp_fd; else fd = iface->raw_fd; - bytes = read(fd, data, len); + bytes = recvmsg(fd, &msg, 0); if (bytes == -1) return errno == EAGAIN ? 0 : -1; + if (partialcsum != NULL) { + *partialcsum = 0; +#ifdef PACKET_AUXDATA + for (cmsg = CMSG_FIRSTHDR(&msg); + cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level == SOL_PACKET && + cmsg->cmsg_type == PACKET_AUXDATA) { + aux = (void *)CMSG_DATA(cmsg); + *partialcsum = aux->tp_status & + TP_STATUS_CSUMNOTREADY; + } + } +#endif + } return bytes; } diff --git a/net.c b/net.c index e26b8d48..e87c9c84 100644 --- a/net.c +++ b/net.c @@ -1,6 +1,6 @@ /* * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2010 Roy Marples + * Copyright (c) 2006-2011 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -726,7 +726,8 @@ get_udp_data(const uint8_t **data, const uint8_t *udp) } int -valid_udp_packet(const uint8_t *data, size_t data_len, struct in_addr *from) +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; @@ -754,19 +755,22 @@ valid_udp_packet(const uint8_t *data, size_t data_len, struct in_addr *from) errno = EINVAL; return -1; } - 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; + + 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 16ba1683..6941c4ce 100644 --- a/net.h +++ b/net.h @@ -140,14 +140,14 @@ 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 valid_udp_packet(const uint8_t *, size_t, struct in_addr *, int noudpcsum); 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); +ssize_t get_raw_packet(struct interface *, int, void *, ssize_t, int *); int init_sockets(void); int open_link_socket(void);