1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2013 Intel Corporation. All rights reserved.
6 /* Make sure the net/if.h header is included before any linux/ one */
9 #include <linux/filter.h>
10 #include <linux/if_infiniband.h>
11 #include <linux/if_packet.h>
12 #include <net/ethernet.h>
13 #include <net/if_arp.h>
17 #include "dhcp-network.h"
18 #include "dhcp-protocol.h"
20 #include "unaligned.h"
22 static int _bind_raw_socket(
24 union sockaddr_union
*link
,
26 const struct hw_addr_data
*hw_addr
,
27 const struct hw_addr_data
*bcast_addr
,
37 assert(IN_SET(arp_type
, ARPHRD_ETHER
, ARPHRD_INFINIBAND
));
41 assert(hw_addr
->length
== ETH_ALEN
);
42 assert(bcast_addr
->length
== ETH_ALEN
);
44 case ARPHRD_INFINIBAND
:
45 assert(hw_addr
->length
== 0);
46 assert(bcast_addr
->length
== INFINIBAND_ALEN
);
52 struct sock_filter filter
[] = {
53 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_LEN
, 0), /* A <- packet length */
54 BPF_JUMP(BPF_JMP
+ BPF_JGE
+ BPF_K
, sizeof(DHCPPacket
), 1, 0), /* packet >= DHCPPacket ? */
55 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
56 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, ip
.protocol
)), /* A <- IP protocol */
57 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, IPPROTO_UDP
, 1, 0), /* IP protocol == UDP ? */
58 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
59 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, ip
.frag_off
)), /* A <- Flags */
60 BPF_STMT(BPF_ALU
+ BPF_AND
+ BPF_K
, 0x20), /* A <- A & 0x20 (More Fragments bit) */
61 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 1, 0), /* A == 0 ? */
62 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
63 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, offsetof(DHCPPacket
, ip
.frag_off
)), /* A <- Flags + Fragment offset */
64 BPF_STMT(BPF_ALU
+ BPF_AND
+ BPF_K
, 0x1fff), /* A <- A & 0x1fff (Fragment offset) */
65 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 1, 0), /* A == 0 ? */
66 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
67 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, offsetof(DHCPPacket
, udp
.dest
)), /* A <- UDP destination port */
68 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, port
, 1, 0), /* UDP destination port == DHCP client port ? */
69 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
70 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.op
)), /* A <- DHCP op */
71 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, BOOTREPLY
, 1, 0), /* op == BOOTREPLY ? */
72 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
73 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.htype
)), /* A <- DHCP header type */
74 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, arp_type
, 1, 0), /* header type == arp_type ? */
75 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
76 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.xid
)), /* A <- client identifier */
77 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, xid
, 1, 0), /* client identifier == xid ? */
78 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
79 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.hlen
)), /* A <- MAC address length */
80 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, (uint8_t) hw_addr
->length
, 1, 0), /* address length == hw_addr->length ? */
81 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
83 /* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally
84 * compare chaddr for ETH_ALEN bytes. */
85 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, ETH_ALEN
, 0, 8), /* A (the MAC address length) == ETH_ALEN ? */
86 BPF_STMT(BPF_LDX
+ BPF_IMM
, unaligned_read_be32(hw_addr
->bytes
)), /* X <- 4 bytes of client's MAC */
87 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.chaddr
)), /* A <- 4 bytes of MAC from dhcp.chaddr */
88 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_X
, 0, 1, 0), /* A == X ? */
89 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
90 BPF_STMT(BPF_LDX
+ BPF_IMM
, unaligned_read_be16(hw_addr
->bytes
+ 4)), /* X <- remainder of client's MAC */
91 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.chaddr
) + 4), /* A <- remainder of MAC from dhcp.chaddr */
92 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_X
, 0, 1, 0), /* A == X ? */
93 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
95 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.magic
)), /* A <- DHCP magic cookie */
96 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, DHCP_MAGIC_COOKIE
, 1, 0), /* cookie == DHCP magic cookie ? */
97 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
98 BPF_STMT(BPF_RET
+ BPF_K
, UINT32_MAX
), /* accept */
100 struct sock_fprog fprog
= {
101 .len
= ELEMENTSOF(filter
),
104 _cleanup_close_
int s
= -EBADF
;
107 s
= socket(AF_PACKET
, SOCK_DGRAM
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, 0);
111 r
= setsockopt_int(s
, SOL_PACKET
, PACKET_AUXDATA
, true);
115 r
= setsockopt(s
, SOL_SOCKET
, SO_ATTACH_FILTER
, &fprog
, sizeof(fprog
));
119 r
= setsockopt_int(s
, SOL_SOCKET
, SO_TIMESTAMP
, true);
123 if (so_priority_set
) {
124 r
= setsockopt_int(s
, SOL_SOCKET
, SO_PRIORITY
, so_priority
);
129 link
->ll
= (struct sockaddr_ll
) {
130 .sll_family
= AF_PACKET
,
131 .sll_protocol
= htobe16(ETH_P_IP
),
132 .sll_ifindex
= ifindex
,
133 .sll_hatype
= htobe16(arp_type
),
134 .sll_halen
= bcast_addr
->length
,
136 /* We may overflow link->ll. link->ll_buffer ensures we have enough space. */
137 memcpy(link
->ll
.sll_addr
, bcast_addr
->bytes
, bcast_addr
->length
);
139 r
= bind(s
, &link
->sa
, SOCKADDR_LL_LEN(link
->ll
));
146 int dhcp_network_bind_raw_socket(
148 union sockaddr_union
*link
,
150 const struct hw_addr_data
*hw_addr
,
151 const struct hw_addr_data
*bcast_addr
,
154 bool so_priority_set
,
157 static struct hw_addr_data default_eth_bcast
= {
159 .ether
= {{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }},
160 }, default_ib_bcast
= {
161 .length
= INFINIBAND_ALEN
,
163 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b,
164 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165 0xff, 0xff, 0xff, 0xff
175 return _bind_raw_socket(ifindex
, link
, xid
,
177 (bcast_addr
&& !hw_addr_is_null(bcast_addr
)) ? bcast_addr
: &default_eth_bcast
,
178 arp_type
, port
, so_priority_set
, so_priority
);
180 case ARPHRD_INFINIBAND
:
181 return _bind_raw_socket(ifindex
, link
, xid
,
183 (bcast_addr
&& !hw_addr_is_null(bcast_addr
)) ? bcast_addr
: &default_ib_bcast
,
184 arp_type
, port
, so_priority_set
, so_priority
);
190 int dhcp_network_bind_udp_socket(int ifindex
, be32_t address
, uint16_t port
, int ip_service_type
) {
191 union sockaddr_union src
= {
192 .in
.sin_family
= AF_INET
,
193 .in
.sin_port
= htobe16(port
),
194 .in
.sin_addr
.s_addr
= address
,
196 _cleanup_close_
int s
= -EBADF
;
199 s
= socket(AF_INET
, SOCK_DGRAM
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, 0);
203 if (ip_service_type
>= 0)
204 r
= setsockopt_int(s
, IPPROTO_IP
, IP_TOS
, ip_service_type
);
206 r
= setsockopt_int(s
, IPPROTO_IP
, IP_TOS
, IPTOS_CLASS_CS6
);
210 r
= setsockopt_int(s
, SOL_SOCKET
, SO_REUSEADDR
, true);
214 r
= setsockopt_int(s
, SOL_SOCKET
, SO_TIMESTAMP
, true);
219 r
= socket_bind_to_ifindex(s
, ifindex
);
224 if (port
== DHCP_PORT_SERVER
) {
225 r
= setsockopt_int(s
, SOL_SOCKET
, SO_BROADCAST
, true);
228 if (address
== INADDR_ANY
) {
229 /* IP_PKTINFO filter should not be applied when packets are
230 allowed to enter/leave through the interface other than
231 DHCP server sits on(BindToInterface option). */
232 r
= setsockopt_int(s
, IPPROTO_IP
, IP_PKTINFO
, true);
237 r
= setsockopt_int(s
, IPPROTO_IP
, IP_FREEBIND
, true);
242 if (bind(s
, &src
.sa
, sizeof(src
.in
)) < 0)
248 int dhcp_network_send_raw_socket(
250 const union sockaddr_union
*link
,
254 /* Do not add assert(s >= 0) here, as this is called in fuzz-dhcp-server, and in that case this
255 * function should fail with negative errno. */
261 if (sendto(s
, packet
, len
, 0, &link
->sa
, SOCKADDR_LL_LEN(link
->ll
)) < 0)
267 int dhcp_network_send_udp_socket(
274 union sockaddr_union dest
= {
275 .in
.sin_family
= AF_INET
,
276 .in
.sin_port
= htobe16(port
),
277 .in
.sin_addr
.s_addr
= address
,
284 if (sendto(s
, packet
, len
, 0, &dest
.sa
, sizeof(dest
.in
)) < 0)