| /* |
| * dhcpcd - DHCP client daemon |
| * Copyright 2006-2008 Roy Marples <roy@marples.name> |
| * All rights reserved |
| |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #include <sys/types.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <sys/time.h> |
| |
| #include <net/if.h> |
| #include <net/if_arp.h> |
| #include <arpa/inet.h> |
| #include <netinet/in_systm.h> |
| #ifdef __linux__ |
| #include <netinet/ether.h> |
| #include <netpacket/packet.h> |
| #endif |
| #include <netinet/in.h> |
| #include <netinet/ip.h> |
| #define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */ |
| #include <netinet/udp.h> |
| #undef __FAVOR_BSD |
| #ifdef SIOCGIFMEDIA |
| #include <net/if_media.h> |
| #endif |
| #include <arpa/inet.h> |
| #ifdef AF_LINK |
| # include <net/if_dl.h> |
| #endif |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include "config.h" |
| #include "common.h" |
| #include "dhcp.h" |
| #include "logger.h" |
| #include "net.h" |
| #include "signals.h" |
| |
| 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 < 0 || 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) |
| { |
| static char buffer[(HWADDR_LEN * 3) + 1]; |
| char *p = buffer; |
| size_t i; |
| |
| for (i = 0; i < hwlen && i < HWADDR_LEN; i++) { |
| if (i > 0) |
| *p ++= ':'; |
| p += snprintf(p, 3, "%.2x", hwaddr[i]); |
| } |
| |
| *p ++= '\0'; |
| |
| return buffer; |
| } |
| |
| size_t |
| hwaddr_aton(unsigned char *buffer, const char *addr) |
| { |
| char c[3]; |
| const char *p = addr; |
| unsigned char *bp = buffer; |
| size_t len = 0; |
| |
| c[2] = '\0'; |
| while (*p) { |
| c[0] = *p++; |
| c[1] = *p++; |
| /* Ensure that digits are hex */ |
| if (isxdigit((unsigned char)c[0]) == 0 || |
| isxdigit((unsigned char)c[1]) == 0) |
| { |
| errno = EINVAL; |
| return 0; |
| } |
| /* We should have at least two entries 00:01 */ |
| if (len == 0 && *p == '\0') { |
| errno = EINVAL; |
| return 0; |
| } |
| /* Ensure that next data is EOL or a seperator with data */ |
| if (!(*p == '\0' || (*p == ':' && *(p + 1) != '\0'))) { |
| errno = EINVAL; |
| return 0; |
| } |
| if (*p) |
| p++; |
| if (bp) |
| *bp++ = (unsigned char)strtol(c, NULL, 16); |
| len++; |
| } |
| return len; |
| } |
| |
| int |
| do_interface(const char *ifname, |
| _unused unsigned char *hwaddr, _unused size_t *hwlen, |
| struct in_addr *addr, struct in_addr *net, int get) |
| { |
| int s; |
| struct ifconf ifc; |
| int retval = 0, found = 0; |
| int len = 10 * sizeof(struct ifreq); |
| int lastlen = 0; |
| char *p; |
| union { |
| char *buffer; |
| struct ifreq *ifr; |
| } ifreqs; |
| struct sockaddr_in address; |
| struct ifreq *ifr; |
| struct sockaddr_in netmask; |
| #ifdef AF_LINK |
| struct sockaddr_dl *sdl; |
| #endif |
| |
| if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) |
| return -1; |
| |
| /* Not all implementations return the needed buffer size for |
| * SIOGIFCONF so we loop like so for all until it works */ |
| memset(&ifc, 0, sizeof(ifc)); |
| for (;;) { |
| ifc.ifc_len = len; |
| ifc.ifc_buf = xmalloc((size_t)len); |
| if (ioctl(s, SIOCGIFCONF, &ifc) == -1) { |
| if (errno != EINVAL || lastlen != 0) { |
| close(s); |
| free(ifc.ifc_buf); |
| return -1; |
| } |
| } else { |
| if (ifc.ifc_len == lastlen) |
| break; |
| lastlen = ifc.ifc_len; |
| } |
| |
| free(ifc.ifc_buf); |
| ifc.ifc_buf = NULL; |
| len *= 2; |
| } |
| |
| for (p = (char *)ifc.ifc_buf; p < (char *)ifc.ifc_buf + ifc.ifc_len;) { |
| /* Cast the ifc buffer to an ifreq cleanly */ |
| ifreqs.buffer = p; |
| ifr = ifreqs.ifr; |
| |
| #ifndef __linux__ |
| if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru)) |
| p += offsetof(struct ifreq, ifr_ifru) + |
| ifr->ifr_addr.sa_len; |
| else |
| #endif |
| p += sizeof(*ifr); |
| |
| if (strcmp(ifname, ifr->ifr_name) != 0) |
| continue; |
| |
| found = 1; |
| |
| #ifdef AF_LINK |
| if (hwaddr && hwlen && ifr->ifr_addr.sa_family == AF_LINK) { |
| sdl = xmalloc(ifr->ifr_addr.sa_len); |
| memcpy(sdl, &ifr->ifr_addr, ifr->ifr_addr.sa_len); |
| *hwlen = sdl->sdl_alen; |
| memcpy(hwaddr, LLADDR(sdl), *hwlen); |
| free(sdl); |
| retval = 1; |
| break; |
| } |
| #endif |
| |
| if (ifr->ifr_addr.sa_family == AF_INET) { |
| memcpy(&address, &ifr->ifr_addr, sizeof(address)); |
| if (ioctl(s, SIOCGIFNETMASK, ifr) == -1) |
| continue; |
| memcpy(&netmask, &ifr->ifr_addr, sizeof(netmask)); |
| if (get) { |
| addr->s_addr = address.sin_addr.s_addr; |
| net->s_addr = netmask.sin_addr.s_addr; |
| retval = 1; |
| break; |
| } else { |
| if (address.sin_addr.s_addr == addr->s_addr && |
| (!net || |
| netmask.sin_addr.s_addr == net->s_addr)) |
| { |
| retval = 1; |
| break; |
| } |
| } |
| } |
| |
| } |
| |
| if (!found) |
| errno = ENXIO; |
| close(s); |
| free(ifc.ifc_buf); |
| return retval; |
| } |
| |
| int |
| up_interface(const char *ifname) |
| { |
| int s; |
| struct ifreq ifr; |
| int retval = -1; |
| #ifdef __linux__ |
| char *p; |
| #endif |
| |
| if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) |
| return -1; |
| memset(&ifr, 0, sizeof(ifr)); |
| strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| #ifdef __linux__ |
| /* We can only bring the real interface up */ |
| if ((p = strchr(ifr.ifr_name, ':'))) |
| *p = '\0'; |
| #endif |
| if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0) { |
| if ((ifr.ifr_flags & IFF_UP)) |
| retval = 0; |
| else { |
| ifr.ifr_flags |= IFF_UP; |
| if (ioctl(s, SIOCSIFFLAGS, &ifr) == 0) |
| retval = 0; |
| } |
| } |
| close(s); |
| return retval; |
| } |
| |
| int |
| carrier_status(const char *ifname) |
| { |
| int s; |
| struct ifreq ifr; |
| int retval = -1; |
| #ifdef SIOCGIFMEDIA |
| struct ifmediareq ifmr; |
| #endif |
| #ifdef __linux__ |
| char *p; |
| #endif |
| |
| if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) |
| return -1; |
| memset(&ifr, 0, sizeof(ifr)); |
| strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| #ifdef __linux__ |
| /* We can only test the real interface up */ |
| if ((p = strchr(ifr.ifr_name, ':'))) |
| *p = '\0'; |
| #endif |
| if ((retval = ioctl(s, SIOCGIFFLAGS, &ifr)) == 0) { |
| if (ifr.ifr_flags & IFF_UP && ifr.ifr_flags & IFF_RUNNING) |
| retval = 1; |
| else |
| retval = 0; |
| } |
| |
| #ifdef SIOCGIFMEDIA |
| if (retval == 1) { |
| memset(&ifmr, 0, sizeof(ifmr)); |
| strncpy(ifmr.ifm_name, ifr.ifr_name, sizeof(ifmr.ifm_name)); |
| if (ioctl(s, SIOCGIFMEDIA, &ifmr) != -1 && |
| ifmr.ifm_status & IFM_AVALID) |
| { |
| if (!(ifmr.ifm_status & IFM_ACTIVE)) |
| retval = 0; |
| } |
| } |
| #endif |
| close(s); |
| return retval; |
| } |
| |
| struct interface * |
| read_interface(const char *ifname, _unused int metric) |
| { |
| int s; |
| struct ifreq ifr; |
| struct interface *iface = NULL; |
| unsigned char *hwaddr = NULL; |
| size_t hwlen = 0; |
| sa_family_t family = 0; |
| |
| memset(&ifr, 0, sizeof(ifr)); |
| strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| |
| if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) |
| return NULL; |
| |
| #ifdef __linux__ |
| strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1) |
| goto eexit; |
| |
| switch (ifr.ifr_hwaddr.sa_family) { |
| case ARPHRD_ETHER: |
| case ARPHRD_IEEE802: |
| hwlen = ETHER_ADDR_LEN; |
| break; |
| case ARPHRD_IEEE1394: |
| hwlen = EUI64_ADDR_LEN; |
| case ARPHRD_INFINIBAND: |
| hwlen = INFINIBAND_ADDR_LEN; |
| break; |
| } |
| |
| hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN); |
| memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, hwlen); |
| family = ifr.ifr_hwaddr.sa_family; |
| #else |
| ifr.ifr_metric = metric; |
| strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| if (ioctl(s, SIOCSIFMETRIC, &ifr) == -1) |
| goto eexit; |
| |
| hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN); |
| if (do_interface(ifname, hwaddr, &hwlen, NULL, NULL, 0) != 1) |
| goto eexit; |
| |
| family = ARPHRD_ETHER; |
| #endif |
| |
| strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| if (ioctl(s, SIOCGIFMTU, &ifr) == -1) |
| goto eexit; |
| |
| /* Ensure that the MTU is big enough for DHCP */ |
| if (ifr.ifr_mtu < MTU_MIN) { |
| ifr.ifr_mtu = MTU_MIN; |
| strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| if (ioctl(s, SIOCSIFMTU, &ifr) == -1) |
| goto eexit; |
| } |
| |
| if (up_interface(ifname) != 0) |
| goto eexit; |
| |
| iface = xzalloc(sizeof(*iface)); |
| strlcpy(iface->name, ifname, IF_NAMESIZE); |
| snprintf(iface->leasefile, PATH_MAX, LEASEFILE, ifname); |
| memcpy(&iface->hwaddr, hwaddr, hwlen); |
| iface->hwlen = hwlen; |
| |
| iface->family = family; |
| iface->arpable = !(ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)); |
| |
| /* 0 is a valid fd, so init to -1 */ |
| iface->raw_fd = -1; |
| iface->udp_fd = -1; |
| iface->arp_fd = -1; |
| iface->link_fd = -1; |
| |
| eexit: |
| close(s); |
| free(hwaddr); |
| return iface; |
| } |
| |
| int |
| do_mtu(const char *ifname, short int mtu) |
| { |
| struct ifreq ifr; |
| int r; |
| int s; |
| |
| if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) |
| return -1; |
| |
| memset(&ifr, 0, sizeof(ifr)); |
| strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
| ifr.ifr_mtu = mtu; |
| r = ioctl(s, mtu ? SIOCSIFMTU : SIOCGIFMTU, &ifr); |
| close(s); |
| if (r == -1) |
| 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; |
| union sockunion { |
| struct sockaddr sa; |
| struct sockaddr_in sin; |
| } su; |
| int n; |
| #ifdef SO_BINDTODEVICE |
| struct ifreq ifr; |
| #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)); |
| 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; |
| memset(&su, 0, sizeof(su)); |
| su.sin.sin_family = AF_INET; |
| su.sin.sin_port = htons(DHCP_CLIENT_PORT); |
| su.sin.sin_addr.s_addr = iface->addr.s_addr; |
| if (bind(s, &su.sa, sizeof(su)) == -1) |
| goto eexit; |
| |
| iface->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) |
| { |
| union sockunion { |
| struct sockaddr sa; |
| struct sockaddr_in sin; |
| } su; |
| |
| memset(&su, 0, sizeof(su)); |
| su.sin.sin_family = AF_INET; |
| su.sin.sin_addr.s_addr = to.s_addr; |
| su.sin.sin_port = htons(DHCP_SERVER_PORT); |
| |
| return sendto(iface->udp_fd, data, len, 0, &su.sa, sizeof(su)); |
| } |
| |
| 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 = 5; |
| ip->ip_id = 0; |
| ip->ip_tos = IPTOS_LOWDELAY; |
| ip->ip_len = htons (sizeof(*ip) + sizeof(*udp) + length); |
| ip->ip_id = 0; |
| ip->ip_off = htons(IP_DF); /* Don't fragment */ |
| ip->ip_ttl = IPDEFTTL; |
| |
| 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) |
| { |
| struct udp_dhcp_packet packet; |
| uint16_t bytes; |
| uint16_t ipsum; |
| uint16_t iplen; |
| uint16_t udpsum; |
| struct in_addr source; |
| struct in_addr dest; |
| int retval = 0; |
| |
| memcpy(&packet, data, sizeof(packet)); |
| bytes = ntohs(packet.ip.ip_len); |
| ipsum = packet.ip.ip_sum; |
| iplen = packet.ip.ip_len; |
| udpsum = packet.udp.uh_sum; |
| |
| if (0 != checksum(&packet.ip, sizeof(packet.ip))) { |
| errno = EINVAL; |
| return -1; |
| } |
| |
| packet.ip.ip_sum = 0; |
| memcpy(&source, &packet.ip.ip_src, sizeof(packet.ip.ip_src)); |
| memcpy(&dest, &packet.ip.ip_dst, sizeof(packet.ip.ip_dst)); |
| memset(&packet.ip, 0, sizeof(packet.ip)); |
| packet.udp.uh_sum = 0; |
| |
| packet.ip.ip_p = IPPROTO_UDP; |
| memcpy(&packet.ip.ip_src, &source, sizeof(packet.ip.ip_src)); |
| memcpy(&packet.ip.ip_dst, &dest, sizeof(packet.ip.ip_dst)); |
| packet.ip.ip_len = packet.udp.uh_ulen; |
| if (udpsum && udpsum != checksum(&packet, bytes)) { |
| errno = EINVAL; |
| retval = -1; |
| } |
| |
| return retval; |
| } |
| |
| int |
| send_arp(const struct interface *iface, int op, in_addr_t sip, in_addr_t tip) |
| { |
| struct arphdr *arp; |
| size_t arpsize; |
| uint8_t *p; |
| int retval; |
| |
| arpsize = sizeof(*arp) + 2 * iface->hwlen + 2 * sizeof(sip); |
| arp = xmalloc(arpsize); |
| arp->ar_hrd = htons(iface->family); |
| arp->ar_pro = htons(ETHERTYPE_IP); |
| arp->ar_hln = iface->hwlen; |
| arp->ar_pln = sizeof(sip); |
| arp->ar_op = htons(op); |
| p = (uint8_t *)arp; |
| p += sizeof(*arp); |
| memcpy(p, iface->hwaddr, iface->hwlen); |
| p += iface->hwlen; |
| memcpy(p, &sip, sizeof(sip)); |
| p += sizeof(sip); |
| /* ARP requests should ignore this */ |
| retval = iface->hwlen; |
| while (retval--) |
| *p++ = '\0'; |
| memcpy(p, &tip, sizeof(tip)); |
| p += sizeof(tip); |
| retval = send_raw_packet(iface, ETHERTYPE_ARP, arp, arpsize); |
| free(arp); |
| return retval; |
| } |