/* * Packet socket handling glue. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipconfig.h" #include "netdev.h" #include "packet.h" static int pkt_fd = -1; uint16_t cfg_local_port = LOCAL_PORT; uint16_t cfg_remote_port = REMOTE_PORT; int packet_open(void) { int fd, one = 1; if (pkt_fd != -1) return pkt_fd; /* * Get a PACKET socket for IP traffic. */ fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); if (fd == -1) { perror("socket"); return -1; } /* * We want to broadcast */ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) == -1) { perror("SO_BROADCAST"); close(fd); fd = -1; } pkt_fd = fd; return fd; } void packet_close(void) { close(pkt_fd); pkt_fd = -1; } static unsigned int ip_checksum(uint16_t *hdr, int len) { unsigned int chksum = 0; while (len) { chksum += *hdr++; chksum += *hdr++; len--; } chksum = (chksum & 0xffff) + (chksum >> 16); chksum = (chksum & 0xffff) + (chksum >> 16); return (~chksum) & 0xffff; } struct header { struct iphdr ip; struct udphdr udp; } __attribute__ ((packed)); static struct header ipudp_hdrs = { .ip = { .ihl = 5, .version = IPVERSION, .frag_off = __constant_htons(IP_DF), .ttl = 64, .protocol = IPPROTO_UDP, .saddr = INADDR_ANY, .daddr = INADDR_BROADCAST, }, .udp = { .source = __constant_htons(LOCAL_PORT), .dest = __constant_htons(REMOTE_PORT), .len = 0, .check = 0, }, }; #ifdef DEBUG /* Only used with dprintf() */ static char *ntoa(uint32_t addr) { struct in_addr in = { addr }; return inet_ntoa(in); } #endif /* DEBUG */ /* * Send a packet. The options are listed in iov[1...iov_len-1]. * iov[0] is reserved for the bootp packet header. */ int packet_send(struct netdev *dev, struct iovec *iov, int iov_len) { struct sockaddr_ll sll; struct msghdr msg; int i, len = 0; memset(&sll, 0, sizeof(sll)); msg.msg_name = &sll; msg.msg_namelen = sizeof(sll); msg.msg_iov = iov; msg.msg_iovlen = iov_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; if (cfg_local_port != LOCAL_PORT) { ipudp_hdrs.udp.source = htons(cfg_local_port); ipudp_hdrs.udp.dest = htons(cfg_remote_port); } dprintf("\n udp src %d dst %d", ntohs(ipudp_hdrs.udp.source), ntohs(ipudp_hdrs.udp.dest)); dprintf("\n ip src %s ", ntoa(ipudp_hdrs.ip.saddr)); dprintf("dst %s ", ntoa(ipudp_hdrs.ip.daddr)); /* * Glue in the ip+udp header iovec */ iov[0].iov_base = &ipudp_hdrs; iov[0].iov_len = sizeof(struct header); for (i = 0; i < iov_len; i++) len += iov[i].iov_len; sll.sll_family = AF_PACKET; sll.sll_protocol = htons(ETH_P_IP); sll.sll_ifindex = dev->ifindex; sll.sll_hatype = dev->hwtype; sll.sll_pkttype = PACKET_BROADCAST; sll.sll_halen = dev->hwlen; memcpy(sll.sll_addr, dev->hwbrd, dev->hwlen); ipudp_hdrs.ip.tot_len = htons(len); ipudp_hdrs.ip.check = 0; ipudp_hdrs.ip.check = ip_checksum((uint16_t *) & ipudp_hdrs.ip, ipudp_hdrs.ip.ihl); ipudp_hdrs.udp.len = htons(len - sizeof(struct iphdr)); dprintf("\n bytes %d\n", len); return sendmsg(pkt_fd, &msg, 0); } void packet_discard(struct netdev *dev) { struct iphdr iph; struct sockaddr_ll sll; socklen_t sllen = sizeof(sll); sll.sll_ifindex = dev->ifindex; recvfrom(pkt_fd, &iph, sizeof(iph), 0, (struct sockaddr *)&sll, &sllen); } /* * Receive a bootp packet. The options are listed in iov[1...iov_len]. * iov[0] must point to the bootp packet header. * Returns: * -1 = Error, try again later * 0 = Discarded packet (non-DHCP/BOOTP traffic) * >0 = Size of packet */ int packet_recv(struct netdev* dev, struct iovec *iov, int iov_len) { struct iphdr *ip, iph; struct udphdr *udp; struct msghdr msg = { .msg_name = NULL, .msg_namelen = 0, .msg_iov = iov, .msg_iovlen = iov_len, .msg_control = NULL, .msg_controllen = 0, .msg_flags = 0 }; int ret, iphl; struct sockaddr_ll sll; socklen_t sllen = sizeof(sll); sll.sll_ifindex = dev->ifindex; msg.msg_name = &sll; msg.msg_namelen = sllen; ret = recvfrom(pkt_fd, &iph, sizeof(struct iphdr), MSG_PEEK, (struct sockaddr *)&sll, &sllen); if (ret == -1) return -1; if (iph.ihl < 5 || iph.version != IPVERSION) goto discard_pkt; iphl = iph.ihl * 4; ip = malloc(iphl + sizeof(struct udphdr)); if (!ip) goto discard_pkt; udp = (struct udphdr *)((char *)ip + iphl); iov[0].iov_base = ip; iov[0].iov_len = iphl + sizeof(struct udphdr); ret = recvmsg(pkt_fd, &msg, 0); if (ret == -1) goto free_pkt; dprintf("<- bytes %d ", ret); if (ip_checksum((uint16_t *) ip, ip->ihl) != 0) goto free_pkt; dprintf("\n ip src %s ", ntoa(ip->saddr)); dprintf("dst %s ", ntoa(ip->daddr)); if (ntohs(ip->tot_len) > ret || ip->protocol != IPPROTO_UDP) goto free_pkt; ret -= 4 * ip->ihl; dprintf("\n udp src %d dst %d ", ntohs(udp->source), ntohs(udp->dest)); if (udp->source != htons(cfg_remote_port) || udp->dest != htons(cfg_local_port)) goto free_pkt; if (ntohs(udp->len) > ret) goto free_pkt; ret -= sizeof(struct udphdr); free(ip); return ret; free_pkt: dprintf("freed\n"); free(ip); return 0; discard_pkt: dprintf("discarded\n"); packet_discard(dev); return 0; }