]>
git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/bpf.c
1 /* dnsmasq is Copyright (c) 2000-2009 Simon Kelley
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
21 static struct iovec ifconf
= {
26 static struct iovec ifreq
= {
31 int iface_enumerate(void *parm
, int (*ipv4_callback
)(), int (*ipv6_callback
)())
36 int fd
, errsav
, ret
= 0;
40 if ((fd
= socket(PF_INET
, SOCK_DGRAM
, 0)) == -1)
45 len
+= 10*sizeof(struct ifreq
);
47 if (!expand_buf(&ifconf
, len
))
51 ifc
.ifc_buf
= ifconf
.iov_base
;
53 if (ioctl(fd
, SIOCGIFCONF
, &ifc
) == -1)
55 if (errno
!= EINVAL
|| lastlen
!= 0)
60 if (ifc
.ifc_len
== lastlen
)
61 break; /* got a big enough buffer now */
62 lastlen
= ifc
.ifc_len
;
66 for (ptr
= ifc
.ifc_buf
; ptr
< (char *)(ifc
.ifc_buf
+ ifc
.ifc_len
); ptr
+= len
)
68 /* subsequent entries may not be aligned, so copy into
69 an aligned buffer to avoid nasty complaints about
70 unaligned accesses. */
72 len
= sizeof(struct ifreq
);
74 #ifdef HAVE_SOCKADDR_SA_LEN
75 ifr
= (struct ifreq
*)ptr
;
76 if (ifr
->ifr_addr
.sa_len
> sizeof(ifr
->ifr_ifru
))
77 len
= ifr
->ifr_addr
.sa_len
+ offsetof(struct ifreq
, ifr_ifru
);
80 if (!expand_buf(&ifreq
, len
))
83 ifr
= (struct ifreq
*)ifreq
.iov_base
;
84 memcpy(ifr
, ptr
, len
);
86 if (ifr
->ifr_addr
.sa_family
== AF_INET
&& ipv4_callback
)
88 struct in_addr addr
, netmask
, broadcast
;
90 addr
= ((struct sockaddr_in
*) &ifr
->ifr_addr
)->sin_addr
;
91 if (ioctl(fd
, SIOCGIFNETMASK
, ifr
) == -1)
93 netmask
= ((struct sockaddr_in
*) &ifr
->ifr_addr
)->sin_addr
;
94 if (ioctl(fd
, SIOCGIFBRDADDR
, ifr
) != -1)
95 broadcast
= ((struct sockaddr_in
*) &ifr
->ifr_addr
)->sin_addr
;
96 if (!((*ipv4_callback
)(addr
,
97 (int)if_nametoindex(ifr
->ifr_name
),
103 else if (ifr
->ifr_addr
.sa_family
== AF_INET6
&& ipv6_callback
)
105 struct in6_addr
*addr
= &((struct sockaddr_in6
*)&ifr
->ifr_addr
)->sin6_addr
;
106 /* voodoo to clear interface field in address */
107 if (!(daemon
->options
& OPT_NOWILD
) && IN6_IS_ADDR_LINKLOCAL(addr
))
109 addr
->s6_addr
[2] = 0;
110 addr
->s6_addr
[3] = 0;
112 if (!((*ipv6_callback
)(addr
,
113 (int)((struct sockaddr_in6
*)&ifr
->ifr_addr
)->sin6_scope_id
,
114 (int)if_nametoindex(ifr
->ifr_name
),
133 #if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
142 /* useful size which happens to be sufficient */
143 if (expand_buf(&ifreq
, sizeof(struct ifreq
)))
145 sprintf(ifreq
.iov_base
, "/dev/bpf%d", i
++);
146 if ((daemon
->dhcp_raw_fd
= open(ifreq
.iov_base
, O_RDWR
, 0)) != -1)
150 die(_("cannot create DHCP BPF socket: %s"), NULL
, EC_BADNET
);
154 void send_via_bpf(struct dhcp_packet
*mess
, size_t len
,
155 struct in_addr iface_addr
, struct ifreq
*ifr
)
157 /* Hairy stuff, packet either has to go to the
158 net broadcast or the destination can't reply to ARP yet,
159 but we do know the physical address.
160 Build the packet by steam, and send directly, bypassing
161 the kernel IP stack */
163 struct ether_header ether
;
166 u16 uh_sport
; /* source port */
167 u16 uh_dport
; /* destination port */
168 u16 uh_ulen
; /* udp length */
169 u16 uh_sum
; /* udp checksum */
175 /* Only know how to do ethernet on *BSD */
176 if (mess
->htype
!= ARPHRD_ETHER
|| mess
->hlen
!= ETHER_ADDR_LEN
)
178 my_syslog(MS_DHCP
| LOG_WARNING
, _("DHCP request for unsupported hardware type (%d) received on %s"),
179 mess
->htype
, ifr
->ifr_name
);
183 ifr
->ifr_addr
.sa_family
= AF_LINK
;
184 if (ioctl(daemon
->dhcpfd
, SIOCGIFADDR
, ifr
) < 0)
187 memcpy(ether
.ether_shost
, LLADDR((struct sockaddr_dl
*)&ifr
->ifr_addr
), ETHER_ADDR_LEN
);
188 ether
.ether_type
= htons(ETHERTYPE_IP
);
190 if (ntohs(mess
->flags
) & 0x8000)
192 memset(ether
.ether_dhost
, 255, ETHER_ADDR_LEN
);
193 ip
.ip_dst
.s_addr
= INADDR_BROADCAST
;
197 memcpy(ether
.ether_dhost
, mess
->chaddr
, ETHER_ADDR_LEN
);
198 ip
.ip_dst
.s_addr
= mess
->yiaddr
.s_addr
;
201 ip
.ip_p
= IPPROTO_UDP
;
202 ip
.ip_src
.s_addr
= iface_addr
.s_addr
;
203 ip
.ip_len
= htons(sizeof(struct ip
) +
204 sizeof(struct udphdr
) +
206 ip
.ip_hl
= sizeof(struct ip
) / 4;
210 ip
.ip_off
= htons(0x4000); /* don't fragment */
211 ip
.ip_ttl
= IPDEFTTL
;
213 for (sum
= 0, i
= 0; i
< sizeof(struct ip
) / 2; i
++)
214 sum
+= ((u16
*)&ip
)[i
];
216 sum
= (sum
& 0xffff) + (sum
>> 16);
217 ip
.ip_sum
= (sum
== 0xffff) ? sum
: ~sum
;
219 udp
.uh_sport
= htons(daemon
->dhcp_server_port
);
220 udp
.uh_dport
= htons(daemon
->dhcp_client_port
);
222 ((char *)mess
)[len
] = 0; /* for checksum, in case length is odd. */
224 udp
.uh_ulen
= sum
= htons(sizeof(struct udphdr
) + len
);
225 sum
+= htons(IPPROTO_UDP
);
226 sum
+= ip
.ip_src
.s_addr
& 0xffff;
227 sum
+= (ip
.ip_src
.s_addr
>> 16) & 0xffff;
228 sum
+= ip
.ip_dst
.s_addr
& 0xffff;
229 sum
+= (ip
.ip_dst
.s_addr
>> 16) & 0xffff;
230 for (i
= 0; i
< sizeof(struct udphdr
)/2; i
++)
231 sum
+= ((u16
*)&udp
)[i
];
232 for (i
= 0; i
< (len
+ 1) / 2; i
++)
233 sum
+= ((u16
*)mess
)[i
];
235 sum
= (sum
& 0xffff) + (sum
>> 16);
236 udp
.uh_sum
= (sum
== 0xffff) ? sum
: ~sum
;
238 ioctl(daemon
->dhcp_raw_fd
, BIOCSETIF
, ifr
);
240 iov
[0].iov_base
= ðer
;
241 iov
[0].iov_len
= sizeof(ether
);
242 iov
[1].iov_base
= &ip
;
243 iov
[1].iov_len
= sizeof(ip
);
244 iov
[2].iov_base
= &udp
;
245 iov
[2].iov_len
= sizeof(udp
);
246 iov
[3].iov_base
= mess
;
247 iov
[3].iov_len
= len
;
249 while (writev(daemon
->dhcp_raw_fd
, iov
, 4) == -1 && retry_send());