2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/types.h>
28 #include <sys/ioctl.h>
29 #include <sys/socket.h>
34 #include <arpa/inet.h>
49 #include "bpf-filter.h"
52 open_socket(struct interface
*iface
, int protocol
)
58 struct bpf_version pv
;
59 struct bpf_program pf
;
64 fd
= open(_PATH_BPF
, O_RDWR
| O_NONBLOCK
);
69 device
= xmalloc(sizeof(char) * PATH_MAX
);
71 snprintf(device
, PATH_MAX
, "/dev/bpf%d", n
++);
72 fd
= open(device
, O_RDWR
| O_NONBLOCK
);
73 } while (fd
== -1 && errno
== EBUSY
);
80 if (ioctl(fd
, BIOCVERSION
, &pv
) == -1)
82 if (pv
.bv_major
!= BPF_MAJOR_VERSION
||
83 pv
.bv_minor
< BPF_MINOR_VERSION
) {
84 syslog(LOG_ERR
, "BPF version mismatch - recompile");
88 memset(&ifr
, 0, sizeof(ifr
));
89 strlcpy(ifr
.ifr_name
, iface
->name
, sizeof(ifr
.ifr_name
));
90 if (ioctl(fd
, BIOCSETIF
, &ifr
) == -1)
93 /* Get the required BPF buffer length from the kernel. */
94 if (ioctl(fd
, BIOCGBLEN
, &buf_len
) == -1)
96 if (iface
->buffer_size
!= (size_t)buf_len
) {
98 iface
->buffer_size
= buf_len
;
99 iface
->buffer
= xmalloc(buf_len
);
100 iface
->buffer_len
= iface
->buffer_pos
= 0;
105 if (ioctl(fd
, BIOCIMMEDIATE
, &flags
) == -1)
109 /* Install the DHCP filter */
110 if (protocol
== ETHERTYPE_ARP
) {
111 pf
.bf_insns
= UNCONST(arp_bpf_filter
);
112 pf
.bf_len
= arp_bpf_filter_len
;
113 fdp
= &iface
->arp_fd
;
115 pf
.bf_insns
= UNCONST(dhcp_bpf_filter
);
116 pf
.bf_len
= dhcp_bpf_filter_len
;
117 fdp
= &iface
->raw_fd
;
119 if (ioctl(fd
, BIOCSETF
, &pf
) == -1)
121 if (set_cloexec(fd
) == -1)
132 iface
->buffer
= NULL
;
138 send_raw_packet(const struct interface
*iface
, int protocol
,
139 const void *data
, ssize_t len
)
142 struct ether_header hw
;
145 memset(&hw
, 0, ETHER_HDR_LEN
);
146 memset(&hw
.ether_dhost
, 0xff, ETHER_ADDR_LEN
);
147 hw
.ether_type
= htons(protocol
);
148 iov
[0].iov_base
= &hw
;
149 iov
[0].iov_len
= ETHER_HDR_LEN
;
150 iov
[1].iov_base
= UNCONST(data
);
151 iov
[1].iov_len
= len
;
152 if (protocol
== ETHERTYPE_ARP
)
156 return writev(fd
, iov
, 2);
159 /* BPF requires that we read the entire buffer.
160 * So we pass the buffer in the API so we can loop on >1 packet. */
162 get_raw_packet(struct interface
*iface
, int protocol
,
163 void *data
, ssize_t len
)
166 struct bpf_hdr packet
;
168 const unsigned char *payload
;
170 if (protocol
== ETHERTYPE_ARP
)
176 if (iface
->buffer_len
== 0) {
177 bytes
= read(fd
, iface
->buffer
, iface
->buffer_size
);
179 return errno
== EAGAIN
? 0 : -1;
180 else if ((size_t)bytes
< sizeof(packet
))
182 iface
->buffer_len
= bytes
;
183 iface
->buffer_pos
= 0;
186 memcpy(&packet
, iface
->buffer
+ iface
->buffer_pos
,
188 if (packet
.bh_caplen
!= packet
.bh_datalen
)
189 goto next
; /* Incomplete packet, drop. */
190 if (iface
->buffer_pos
+ packet
.bh_caplen
+ packet
.bh_hdrlen
>
192 goto next
; /* Packet beyond buffer, drop. */
193 payload
= iface
->buffer
+ packet
.bh_hdrlen
+ ETHER_HDR_LEN
;
194 bytes
= packet
.bh_caplen
- ETHER_HDR_LEN
;
197 memcpy(data
, payload
, bytes
);
199 iface
->buffer_pos
+= BPF_WORDALIGN(packet
.bh_hdrlen
+
201 if (iface
->buffer_pos
>= iface
->buffer_len
)
202 iface
->buffer_len
= iface
->buffer_pos
= 0;