1/* 2 * Copyright (c) 1999, 2000, 2010-2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * bootp_transmit.c 26 * - send a bootp reques using a socket or BPF 27 */ 28/* 29 * Modification History 30 * 31 * May 11, 2000 Dieter Siegmund (dieter@apple.com) 32 * - created 33 */ 34 35#include <stdlib.h> 36#include <unistd.h> 37#include <string.h> 38#include <stdio.h> 39#include <sys/types.h> 40#include <sys/errno.h> 41#include <sys/socket.h> 42#include <ctype.h> 43#include <net/if.h> 44#include <net/ethernet.h> 45#include <net/firewire.h> 46#include <net/if_arp.h> 47#include <netinet/in.h> 48#include <netinet/udp.h> 49#include <netinet/in_systm.h> 50#include <netinet/ip.h> 51#include <arpa/inet.h> 52#include "IPConfigurationLog.h" 53 54#include "bootp_transmit.h" 55#include "bpflib.h" 56#include "in_cksum.h" 57 58typedef struct { 59 struct ip ip; 60 struct udphdr udp; 61} ip_udp_header_t; 62 63typedef struct { 64 struct in_addr src_ip; 65 struct in_addr dest_ip; 66 char zero; 67 char proto; 68 unsigned short length; 69} udp_pseudo_hdr_t; 70 71 72static int 73get_bpf_fd(const char * if_name) 74{ 75 int bpf_fd; 76 77 bpf_fd = bpf_new(); 78 if (bpf_fd < 0) { 79 /* BPF transmit unavailable */ 80 IPConfigLog(LOG_ERR, "Transmitter: bpf_fd() failed, %s (%d)", 81 strerror(errno), errno); 82 } 83 else if (bpf_filter_receive_none(bpf_fd) < 0) { 84 IPConfigLog(LOG_ERR, "Transmitter: failed to set filter, %s (%d)", 85 strerror(errno), errno); 86 bpf_dispose(bpf_fd); 87 bpf_fd = -1; 88 } 89 else if (bpf_setif(bpf_fd, if_name) < 0) { 90 if (errno != ENXIO) { 91 IPConfigLog(LOG_ERR, "Transmitter: bpf_setif(%s) failed: %s (%d)", 92 if_name, 93 strerror(errno), errno); 94 } 95 bpf_dispose(bpf_fd); 96 bpf_fd = -1; 97 } 98 99 return (bpf_fd); 100} 101 102int 103bootp_transmit(int sockfd, void * sendbuf, 104 const char * if_name, int hwtype, const void * hwaddr, int hwlen, 105 struct in_addr dest_ip, 106 struct in_addr src_ip, 107 u_short dest_port, 108 u_short src_port, 109 const void * data, int len) 110{ 111 static int first = 1; 112 static int ip_id = 0; 113 int bpf_fd = -1; 114 int status = 0; 115 116 if (first) { 117 first = 0; 118 ip_id = arc4random(); 119 } 120 121 if ((hwtype == ARPHRD_ETHER || hwtype == ARPHRD_IEEE1394) 122 && (ntohl(dest_ip.s_addr) == INADDR_BROADCAST 123 || hwaddr != NULL)) { 124 bpf_fd = get_bpf_fd(if_name); 125 if (bpf_fd < 0) { 126 status = -1; 127 } 128 else { 129 int frame_length; 130 ip_udp_header_t * ip_udp; 131 char * payload; 132 udp_pseudo_hdr_t * udp_pseudo; 133 134 switch (hwtype) { 135 default: 136 case ARPHRD_ETHER: 137 { 138 struct ether_header * eh_p; 139 140 eh_p = (struct ether_header *)sendbuf; 141 ip_udp = (ip_udp_header_t *)(sendbuf + sizeof(*eh_p)); 142 udp_pseudo = (udp_pseudo_hdr_t *)(((char *)&ip_udp->udp) 143 - sizeof(*udp_pseudo)); 144 payload = sendbuf + sizeof(*eh_p) + sizeof(*ip_udp); 145 /* fill in the ethernet header */ 146 if (ntohl(dest_ip.s_addr) == INADDR_BROADCAST) { 147 memset(eh_p->ether_dhost, 0xff, 148 sizeof(eh_p->ether_dhost)); 149 } 150 else { 151 bcopy(hwaddr, eh_p->ether_dhost, 152 sizeof(eh_p->ether_dhost)); 153 } 154 eh_p->ether_type = htons(ETHERTYPE_IP); 155 frame_length = sizeof(*eh_p) + sizeof(*ip_udp) + len; 156 break; 157 } 158 case ARPHRD_IEEE1394: 159 { 160 struct firewire_header * fh_p; 161 162 /* fill in the firewire header */ 163 fh_p = (struct firewire_header *)sendbuf; 164 memset(fh_p->firewire_dhost, 0xff, 165 sizeof(fh_p->firewire_dhost)); 166 167 fh_p->firewire_type = htons(ETHERTYPE_IP); 168 ip_udp = (ip_udp_header_t *)(sendbuf + sizeof(*fh_p)); 169 udp_pseudo = (udp_pseudo_hdr_t *)(((char *)&ip_udp->udp) 170 - sizeof(*udp_pseudo)); 171 payload = sendbuf + sizeof(*fh_p) + sizeof(*ip_udp); 172 frame_length = sizeof(*fh_p) + sizeof(*ip_udp) + len; 173 break; 174 } 175 } 176 177 /* copy the data */ 178 bcopy(data, payload, len); 179 180 /* fill in udp pseudo header */ 181 bcopy(&src_ip, &udp_pseudo->src_ip, sizeof(src_ip)); 182 bcopy(&dest_ip, &udp_pseudo->dest_ip, sizeof(dest_ip)); 183 udp_pseudo->zero = 0; 184 udp_pseudo->proto = IPPROTO_UDP; 185 udp_pseudo->length = htons(sizeof(ip_udp->udp) + len); 186 187 /* fill in UDP header */ 188 ip_udp->udp.uh_sport = htons(src_port); 189 ip_udp->udp.uh_dport = htons(dest_port); 190 ip_udp->udp.uh_ulen = htons(sizeof(ip_udp->udp) + len); 191 ip_udp->udp.uh_sum = 0; 192 ip_udp->udp.uh_sum = in_cksum(udp_pseudo, sizeof(*udp_pseudo) + 193 sizeof(ip_udp->udp) + len); 194 195 /* fill in IP header */ 196 bzero(ip_udp, sizeof(ip_udp->ip)); 197 ip_udp->ip.ip_v = IPVERSION; 198 ip_udp->ip.ip_hl = sizeof(struct ip) >> 2; 199 ip_udp->ip.ip_ttl = MAXTTL; 200 ip_udp->ip.ip_p = IPPROTO_UDP; 201 bcopy(&src_ip, &ip_udp->ip.ip_src, sizeof(src_ip)); 202 bcopy(&dest_ip, &ip_udp->ip.ip_dst, sizeof(dest_ip)); 203 ip_udp->ip.ip_len = htons(sizeof(*ip_udp) + len); 204 ip_udp->ip.ip_id = htons(ip_id++); 205 /* compute the IP checksum */ 206 ip_udp->ip.ip_sum = 0; /* needs to be zero for checksum */ 207 ip_udp->ip.ip_sum = in_cksum(&ip_udp->ip, sizeof(ip_udp->ip)); 208 209 status = bpf_write(bpf_fd, sendbuf, frame_length); 210 if (status < 0) { 211 IPConfigLogFL(LOG_ERR, 212 "bpf_write(%s) failed: %s (%d)", 213 if_name, strerror(errno), errno); 214 } 215 } 216 } 217 else if (sockfd >= 0) { /* send using socket */ 218 struct sockaddr_in dst; 219 ssize_t send_status; 220 221 bzero(&dst, sizeof(dst)); 222 dst.sin_len = sizeof(struct sockaddr_in); 223 dst.sin_family = AF_INET; 224 dst.sin_port = htons(dest_port); 225 dst.sin_addr = dest_ip; 226 send_status = sendto(sockfd, data, len, 0, 227 (struct sockaddr *)&dst, 228 sizeof(struct sockaddr_in)); 229 if (send_status < len) 230 status = -1; 231 232 } 233 else { 234 IPConfigLogFL(LOG_ERR, "neither bpf nor socket send available"); 235 } 236 237 if (bpf_fd >= 0) { 238 bpf_dispose(bpf_fd); 239 } 240 return (status); 241} 242 243