]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/dhcp-network.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright (C) 2013 Intel Corporation. All rights reserved.
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <net/ethernet.h>
24 #include <net/if_arp.h>
27 #include <sys/socket.h>
28 #include <linux/filter.h>
29 #include <linux/if_infiniband.h>
30 #include <linux/if_packet.h>
32 #include "dhcp-internal.h"
34 #include "socket-util.h"
36 static int _bind_raw_socket(int ifindex
, union sockaddr_union
*link
,
37 uint32_t xid
, const uint8_t *mac_addr
,
39 const uint8_t *bcast_addr
,
40 const struct ether_addr
*eth_mac
,
41 uint16_t arp_type
, uint8_t dhcp_hlen
,
43 struct sock_filter filter
[] = {
44 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_LEN
, 0), /* A <- packet length */
45 BPF_JUMP(BPF_JMP
+ BPF_JGE
+ BPF_K
, sizeof(DHCPPacket
), 1, 0), /* packet >= DHCPPacket ? */
46 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
47 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, ip
.protocol
)), /* A <- IP protocol */
48 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, IPPROTO_UDP
, 1, 0), /* IP protocol == UDP ? */
49 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
50 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, ip
.frag_off
)), /* A <- Flags */
51 BPF_STMT(BPF_ALU
+ BPF_AND
+ BPF_K
, 0x20), /* A <- A & 0x20 (More Fragments bit) */
52 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 1, 0), /* A == 0 ? */
53 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
54 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, offsetof(DHCPPacket
, ip
.frag_off
)), /* A <- Flags + Fragment offset */
55 BPF_STMT(BPF_ALU
+ BPF_AND
+ BPF_K
, 0x1fff), /* A <- A & 0x1fff (Fragment offset) */
56 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 1, 0), /* A == 0 ? */
57 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
58 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, offsetof(DHCPPacket
, udp
.dest
)), /* A <- UDP destination port */
59 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, port
, 1, 0), /* UDP destination port == DHCP client port ? */
60 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
61 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.op
)), /* A <- DHCP op */
62 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, BOOTREPLY
, 1, 0), /* op == BOOTREPLY ? */
63 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
64 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.htype
)), /* A <- DHCP header type */
65 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, arp_type
, 1, 0), /* header type == arp_type ? */
66 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
67 BPF_STMT(BPF_LD
+ BPF_B
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.hlen
)), /* A <- MAC address length */
68 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, dhcp_hlen
, 1, 0), /* address length == dhcp_hlen ? */
69 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
70 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.xid
)), /* A <- client identifier */
71 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, xid
, 1, 0), /* client identifier == xid ? */
72 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
73 BPF_STMT(BPF_LD
+ BPF_IMM
, htobe32(*((unsigned int *) eth_mac
))), /* A <- 4 bytes of client's MAC */
74 BPF_STMT(BPF_MISC
+ BPF_TAX
, 0), /* X <- A */
75 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.chaddr
)), /* A <- 4 bytes of MAC from dhcp.chaddr */
76 BPF_STMT(BPF_ALU
+ BPF_XOR
+ BPF_X
, 0), /* A xor X */
77 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 1, 0), /* A == 0 ? */
78 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
79 BPF_STMT(BPF_LD
+ BPF_IMM
, htobe16(*((unsigned short *) (((char *) eth_mac
) + 4)))), /* A <- remainder of client's MAC */
80 BPF_STMT(BPF_MISC
+ BPF_TAX
, 0), /* X <- A */
81 BPF_STMT(BPF_LD
+ BPF_H
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.chaddr
) + 4), /* A <- remainder of MAC from dhcp.chaddr */
82 BPF_STMT(BPF_ALU
+ BPF_XOR
+ BPF_X
, 0), /* A xor X */
83 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 1, 0), /* A == 0 ? */
84 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
85 BPF_STMT(BPF_LD
+ BPF_W
+ BPF_ABS
, offsetof(DHCPPacket
, dhcp
.magic
)), /* A <- DHCP magic cookie */
86 BPF_JUMP(BPF_JMP
+ BPF_JEQ
+ BPF_K
, DHCP_MAGIC_COOKIE
, 1, 0), /* cookie == DHCP magic cookie ? */
87 BPF_STMT(BPF_RET
+ BPF_K
, 0), /* ignore */
88 BPF_STMT(BPF_RET
+ BPF_K
, 65535), /* return all */
90 struct sock_fprog fprog
= {
91 .len
= ELEMENTSOF(filter
),
94 _cleanup_close_
int s
= -1;
100 s
= socket(AF_PACKET
, SOCK_DGRAM
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, 0);
104 r
= setsockopt(s
, SOL_PACKET
, PACKET_AUXDATA
, &on
, sizeof(on
));
108 r
= setsockopt(s
, SOL_SOCKET
, SO_ATTACH_FILTER
, &fprog
, sizeof(fprog
));
112 link
->ll
= (struct sockaddr_ll
) {
113 .sll_family
= AF_PACKET
,
114 .sll_protocol
= htobe16(ETH_P_IP
),
115 .sll_ifindex
= ifindex
,
116 .sll_hatype
= htobe16(arp_type
),
117 .sll_halen
= mac_addr_len
,
119 memcpy(link
->ll
.sll_addr
, bcast_addr
, mac_addr_len
);
121 r
= bind(s
, &link
->sa
, SOCKADDR_LL_LEN(link
->ll
));
131 int dhcp_network_bind_raw_socket(int ifindex
, union sockaddr_union
*link
,
132 uint32_t xid
, const uint8_t *mac_addr
,
133 size_t mac_addr_len
, uint16_t arp_type
,
135 static const uint8_t eth_bcast
[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
136 /* Default broadcast address for IPoIB */
137 static const uint8_t ib_bcast
[] = {
138 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b,
139 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
140 0xff, 0xff, 0xff, 0xff
142 struct ether_addr eth_mac
= { { 0, 0, 0, 0, 0, 0 } };
143 const uint8_t *bcast_addr
= NULL
;
144 uint8_t dhcp_hlen
= 0;
146 assert_return(mac_addr_len
> 0, -EINVAL
);
148 if (arp_type
== ARPHRD_ETHER
) {
149 assert_return(mac_addr_len
== ETH_ALEN
, -EINVAL
);
150 memcpy(ð_mac
, mac_addr
, ETH_ALEN
);
151 bcast_addr
= eth_bcast
;
152 dhcp_hlen
= ETH_ALEN
;
153 } else if (arp_type
== ARPHRD_INFINIBAND
) {
154 assert_return(mac_addr_len
== INFINIBAND_ALEN
, -EINVAL
);
155 bcast_addr
= ib_bcast
;
159 return _bind_raw_socket(ifindex
, link
, xid
, mac_addr
, mac_addr_len
,
160 bcast_addr
, ð_mac
, arp_type
, dhcp_hlen
, port
);
163 int dhcp_network_bind_udp_socket(int ifindex
, be32_t address
, uint16_t port
) {
164 union sockaddr_union src
= {
165 .in
.sin_family
= AF_INET
,
166 .in
.sin_port
= htobe16(port
),
167 .in
.sin_addr
.s_addr
= address
,
169 _cleanup_close_
int s
= -1;
170 char ifname
[IF_NAMESIZE
] = "";
171 int r
, on
= 1, tos
= IPTOS_CLASS_CS6
;
173 s
= socket(AF_INET
, SOCK_DGRAM
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, 0);
177 r
= setsockopt(s
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
));
181 r
= setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
));
186 if (if_indextoname(ifindex
, ifname
) == 0)
189 r
= setsockopt(s
, SOL_SOCKET
, SO_BINDTODEVICE
, ifname
, strlen(ifname
));
194 if (address
== INADDR_ANY
) {
195 r
= setsockopt(s
, IPPROTO_IP
, IP_PKTINFO
, &on
, sizeof(on
));
199 r
= setsockopt(s
, SOL_SOCKET
, SO_BROADCAST
, &on
, sizeof(on
));
204 r
= setsockopt(s
, IPPROTO_IP
, IP_FREEBIND
, &on
, sizeof(on
));
209 r
= bind(s
, &src
.sa
, sizeof(src
.in
));
219 int dhcp_network_send_raw_socket(int s
, const union sockaddr_union
*link
,
220 const void *packet
, size_t len
) {
227 r
= sendto(s
, packet
, len
, 0, &link
->sa
, SOCKADDR_LL_LEN(link
->ll
));
234 int dhcp_network_send_udp_socket(int s
, be32_t address
, uint16_t port
,
235 const void *packet
, size_t len
) {
236 union sockaddr_union dest
= {
237 .in
.sin_family
= AF_INET
,
238 .in
.sin_port
= htobe16(port
),
239 .in
.sin_addr
.s_addr
= address
,
247 r
= sendto(s
, packet
, len
, 0, &dest
.sa
, sizeof(dest
.in
));