]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/dhcp-network.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2013 Intel Corporation. All rights reserved.
7 #include <net/ethernet.h>
9 #include <net/if_arp.h>
12 #include <linux/filter.h>
13 #include <linux/if_infiniband.h>
14 #include <linux/if_packet.h>
16 #include "dhcp-internal.h"
18 #include "socket-util.h"
19 #include "unaligned.h"
21 static int _bind_raw_socket(int ifindex
, union sockaddr_union
*link
,
22 uint32_t xid
, const uint8_t *mac_addr
,
24 const uint8_t *bcast_addr
,
25 const struct ether_addr
*eth_mac
,
26 uint16_t arp_type
, uint8_t dhcp_hlen
,
28 struct sock_filter filter
[] = {
29 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_LEN
, 0), /* A <- packet length */
30 BPF_JUMP(BPF_JMP
+ BPF_JGE
+ BPF_K
, sizeof(DHCPPacket
), 1, 0), /* packet >= DHCPPacket ? */
31 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
32 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, ip
.protocol
)), /* A <- IP protocol */
33 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, IPPROTO_UDP
, 1, 0), /* IP protocol == UDP ? */
34 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
35 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, ip
.frag_off
)), /* A <- Flags */
36 BPF_STMT(BPF_ALU
+ BPF_AND
+ BPF_K
, 0x20), /* A <- A & 0x20 (More Fragments bit) */
37 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 1, 0), /* A == 0 ? */
38 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
39 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, offsetof(DHCPPacket
, ip
.frag_off
)), /* A <- Flags + Fragment offset */
40 BPF_STMT(BPF_ALU
+ BPF_AND
+ BPF_K
, 0x1fff), /* A <- A & 0x1fff (Fragment offset) */
41 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 1, 0), /* A == 0 ? */
42 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
43 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, offsetof(DHCPPacket
, udp
.dest
)), /* A <- UDP destination port */
44 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, port
, 1, 0), /* UDP destination port == DHCP client port ? */
45 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
46 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.op
)), /* A <- DHCP op */
47 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, BOOTREPLY
, 1, 0), /* op == BOOTREPLY ? */
48 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
49 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.htype
)), /* A <- DHCP header type */
50 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, arp_type
, 1, 0), /* header type == arp_type ? */
51 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
52 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.xid
)), /* A <- client identifier */
53 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, xid
, 1, 0), /* client identifier == xid ? */
54 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
55 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.hlen
)), /* A <- MAC address length */
56 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, dhcp_hlen
, 1, 0), /* address length == dhcp_hlen ? */
57 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
59 /* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally
60 * compare chaddr for ETH_ALEN bytes. */
61 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, ETH_ALEN
, 0, 12), /* A (the MAC address length) == ETH_ALEN ? */
62 BPF_STMT(BPF_LD
+ BPF_IMM
, unaligned_read_be32(ð_mac
->ether_addr_octet
[0])), /* A <- 4 bytes of client's MAC */
63 BPF_STMT(BPF_MISC
+ BPF_TAX
, 0), /* X <- A */
64 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.chaddr
)), /* A <- 4 bytes of MAC from dhcp.chaddr */
65 BPF_STMT(BPF_ALU
+ BPF_XOR
+ BPF_X
, 0), /* A xor X */
66 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 1, 0), /* A == 0 ? */
67 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
68 BPF_STMT(BPF_LD
+ BPF_IMM
, unaligned_read_be16(ð_mac
->ether_addr_octet
[4])), /* A <- remainder of client's MAC */
69 BPF_STMT(BPF_MISC
+ BPF_TAX
, 0), /* X <- A */
70 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.chaddr
) + 4), /* A <- remainder of MAC from dhcp.chaddr */
71 BPF_STMT(BPF_ALU
+ BPF_XOR
+ BPF_X
, 0), /* A xor X */
72 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 1, 0), /* A == 0 ? */
73 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
75 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.magic
)), /* A <- DHCP magic cookie */
76 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, DHCP_MAGIC_COOKIE
, 1, 0), /* cookie == DHCP magic cookie ? */
77 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
78 BPF_STMT(BPF_RET
+ BPF_K
, 65535), /* return all */
80 struct sock_fprog fprog
= {
81 .len
= ELEMENTSOF(filter
),
84 _cleanup_close_
int s
= -1;
90 s
= socket(AF_PACKET
, SOCK_DGRAM
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, 0);
94 r
= setsockopt_int(s
, SOL_PACKET
, PACKET_AUXDATA
, true);
98 r
= setsockopt(s
, SOL_SOCKET
, SO_ATTACH_FILTER
, &fprog
, sizeof(fprog
));
102 link
->ll
= (struct sockaddr_ll
) {
103 .sll_family
= AF_PACKET
,
104 .sll_protocol
= htobe16(ETH_P_IP
),
105 .sll_ifindex
= ifindex
,
106 .sll_hatype
= htobe16(arp_type
),
107 .sll_halen
= mac_addr_len
,
109 memcpy(link
->ll
.sll_addr
, bcast_addr
, mac_addr_len
);
111 r
= bind(s
, &link
->sa
, SOCKADDR_LL_LEN(link
->ll
));
118 int dhcp_network_bind_raw_socket(int ifindex
, union sockaddr_union
*link
,
119 uint32_t xid
, const uint8_t *mac_addr
,
120 size_t mac_addr_len
, uint16_t arp_type
,
122 static const uint8_t eth_bcast
[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
123 /* Default broadcast address for IPoIB */
124 static const uint8_t ib_bcast
[] = {
125 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b,
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0xff, 0xff, 0xff, 0xff
129 struct ether_addr eth_mac
= { { 0, 0, 0, 0, 0, 0 } };
130 const uint8_t *bcast_addr
= NULL
;
131 uint8_t dhcp_hlen
= 0;
133 if (arp_type
== ARPHRD_ETHER
) {
134 assert_return(mac_addr_len
== ETH_ALEN
, -EINVAL
);
135 memcpy(ð_mac
, mac_addr
, ETH_ALEN
);
136 bcast_addr
= eth_bcast
;
137 dhcp_hlen
= ETH_ALEN
;
138 } else if (arp_type
== ARPHRD_INFINIBAND
) {
139 assert_return(mac_addr_len
== INFINIBAND_ALEN
, -EINVAL
);
140 bcast_addr
= ib_bcast
;
144 return _bind_raw_socket(ifindex
, link
, xid
, mac_addr
, mac_addr_len
,
145 bcast_addr
, ð_mac
, arp_type
, dhcp_hlen
, port
);
148 int dhcp_network_bind_udp_socket(int ifindex
, be32_t address
, uint16_t port
, int ip_service_type
) {
149 union sockaddr_union src
= {
150 .in
.sin_family
= AF_INET
,
151 .in
.sin_port
= htobe16(port
),
152 .in
.sin_addr
.s_addr
= address
,
154 _cleanup_close_
int s
= -1;
157 s
= socket(AF_INET
, SOCK_DGRAM
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, 0);
161 if (ip_service_type
>= 0)
162 r
= setsockopt_int(s
, IPPROTO_IP
, IP_TOS
, ip_service_type
);
164 r
= setsockopt_int(s
, IPPROTO_IP
, IP_TOS
, IPTOS_CLASS_CS6
);
169 r
= setsockopt_int(s
, SOL_SOCKET
, SO_REUSEADDR
, true);
174 r
= socket_bind_to_ifindex(s
, ifindex
);
179 if (address
== INADDR_ANY
) {
180 r
= setsockopt_int(s
, IPPROTO_IP
, IP_PKTINFO
, true);
184 r
= setsockopt_int(s
, SOL_SOCKET
, SO_BROADCAST
, true);
189 r
= setsockopt_int(s
, IPPROTO_IP
, IP_FREEBIND
, true);
194 r
= bind(s
, &src
.sa
, sizeof(src
.in
));
201 int dhcp_network_send_raw_socket(int s
, const union sockaddr_union
*link
,
202 const void *packet
, size_t len
) {
209 r
= sendto(s
, packet
, len
, 0, &link
->sa
, SOCKADDR_LL_LEN(link
->ll
));
216 int dhcp_network_send_udp_socket(int s
, be32_t address
, uint16_t port
,
217 const void *packet
, size_t len
) {
218 union sockaddr_union dest
= {
219 .in
.sin_family
= AF_INET
,
220 .in
.sin_port
= htobe16(port
),
221 .in
.sin_addr
.s_addr
= address
,
229 r
= sendto(s
, packet
, len
, 0, &dest
.sa
, sizeof(dest
.in
));