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