dhcp-network.c revision 085cabf266bfbbc5ebdada9179a8ebe404e540a8
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers/***
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers This file is part of systemd.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers Copyright (C) 2013 Intel Corporation. All rights reserved.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers systemd is free software; you can redistribute it and/or modify it
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers under the terms of the GNU Lesser General Public License as published by
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers the Free Software Foundation; either version 2.1 of the License, or
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers (at your option) any later version.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers systemd is distributed in the hope that it will be useful, but
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers WITHOUT ANY WARRANTY; without even the implied warranty of
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers Lesser General Public License for more details.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers You should have received a copy of the GNU Lesser General Public License
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers along with systemd; If not, see <http://www.gnu.org/licenses/>.
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers***/
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers#include <errno.h>
ad37f393fa97f4274cc3bf97a0d8c388a429037eKay Sievers#include <sys/types.h>
472780d8b1ec3f3f4ff78eb21a013136e5aa1cfeKay Sievers#include <sys/socket.h>
472780d8b1ec3f3f4ff78eb21a013136e5aa1cfeKay Sievers#include <string.h>
472780d8b1ec3f3f4ff78eb21a013136e5aa1cfeKay Sievers#include <linux/if_packet.h>
472780d8b1ec3f3f4ff78eb21a013136e5aa1cfeKay Sievers#include <net/ethernet.h>
472780d8b1ec3f3f4ff78eb21a013136e5aa1cfeKay Sievers#include <net/if_arp.h>
25da63b9dac8f166ebf390ca92d1de18fbfc9d11Kay Sievers#include <stdio.h>
25da63b9dac8f166ebf390ca92d1de18fbfc9d11Kay Sievers#include <unistd.h>
ad37f393fa97f4274cc3bf97a0d8c388a429037eKay Sievers#include <linux/filter.h>
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers#include "socket-util.h"
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers#include "dhcp-internal.h"
ad37f393fa97f4274cc3bf97a0d8c388a429037eKay Sievers
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sieversint dhcp_network_bind_raw_socket(int index, union sockaddr_union *link, uint32_t xid) {
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sievers struct sock_filter filter[] = {
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sievers BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sievers BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0), /* packet >= DHCPPacket ? */
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sievers BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sievers /* TODO: match ip.version */
472780d8b1ec3f3f4ff78eb21a013136e5aa1cfeKay Sievers BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.protocol)), /* A <- IP protocol */
ad37f393fa97f4274cc3bf97a0d8c388a429037eKay Sievers BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0), /* IP protocol == UDP ? */
472780d8b1ec3f3f4ff78eb21a013136e5aa1cfeKay Sievers BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, udp.dest)), /* A <- UDP destination port */
0d6ce9236f61cb991d7e8f2359d818e41ead0cf5Kay Sievers BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_PORT_CLIENT, 1, 0), /* UDP destination port == DHCP client port ? */
0d6ce9236f61cb991d7e8f2359d818e41ead0cf5Kay Sievers BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
0d6ce9236f61cb991d7e8f2359d818e41ead0cf5Kay Sievers BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.op)), /* A <- DHCP op */
0d6ce9236f61cb991d7e8f2359d818e41ead0cf5Kay Sievers BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */
ad37f393fa97f4274cc3bf97a0d8c388a429037eKay Sievers BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.htype)), /* A <- DHCP header type */
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header type == ARPHRD_ETHER ? */
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- mac address length */
ad37f393fa97f4274cc3bf97a0d8c388a429037eKay Sievers BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHER_ADDR_LEN, 1, 0), /* address length == ETHER_ADDR_LEN ? */
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */
de892aea1c486b59e04884268b612081d1660514Kay Sievers BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers /* TODO: match chaddr */
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)), /* A <- DHCP magic cookie */
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0), /* cookie == DHCP magic cookie ? */
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers };
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers struct sock_fprog fprog = {
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers .len = ELEMENTSOF(filter),
decd634e801bee2c554edb35383cc9d43417a850Kay Sievers .filter = filter
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers };
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers int s, one = 1;
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers assert(index > 0);
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers assert(link);
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers if (s < 0)
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers return -errno;
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers link->ll.sll_family = AF_PACKET;
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers link->ll.sll_protocol = htons(ETH_P_IP);
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers link->ll.sll_ifindex = index;
f610d6de38119b372b377ec41b2a6089872d3294Kay Sievers link->ll.sll_halen = ETH_ALEN;
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers memset(link->ll.sll_addr, 0xff, ETH_ALEN);
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers if (setsockopt (s, SOL_PACKET, PACKET_AUXDATA, &one, sizeof(one)) < 0)
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers return -errno;
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) {
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers return -errno;
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers }
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers
de892aea1c486b59e04884268b612081d1660514Kay Sievers if (bind(s, &link->sa, sizeof(link->ll)) < 0) {
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers safe_close(s);
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers return -errno;
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers }
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers return s;
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers}
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sieversint dhcp_network_bind_udp_socket(be32_t address, uint16_t port)
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers{
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers int s;
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers union sockaddr_union src = {
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers .in.sin_family = AF_INET,
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers .in.sin_port = htobe16(port),
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers .in.sin_addr.s_addr = address,
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers };
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers if (s < 0)
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers return -errno;
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers if (bind(s, &src.sa, sizeof(src.in)) < 0) {
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers safe_close(s);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers return -errno;
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers }
984c4348ff14d29c526d3d372daa82e278eeb5b4Kay Sievers
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers return s;
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers}
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
0260944060426d54d9ecb40930baad985cbd02a1Kay Sieversint dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers const void *packet, size_t len)
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers{
a660c63c551b88136ac6176855b5907cc533e848Kay Sievers assert(link);
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers assert(packet);
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers assert(len);
01d183ddae6fb3445c4519cf1d90c6575f17292eKay Sievers
01d183ddae6fb3445c4519cf1d90c6575f17292eKay Sievers if (sendto(s, packet, len, 0, &link->sa, sizeof(link->ll)) < 0)
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers return -errno;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers return 0;
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers}
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sieversint dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers const void *packet, size_t len)
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers{
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers union sockaddr_union dest = {
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers .in.sin_family = AF_INET,
0035597a30d120f70df2dd7da3d6128fb8ba6051Kay Sievers .in.sin_port = htobe16(port),
d23965a64eb5c2c97b839dc2e3e79fc1613994f1Kay Sievers .in.sin_addr.s_addr = address,
137661d87525a3c339afd2804e577532d58d3fbcKay Sievers };
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sievers
de892aea1c486b59e04884268b612081d1660514Kay Sievers if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in)) < 0)
3c123e0899b56c0587db36420da5e049c56d9e19Lukas Nykryn return -errno;
0260944060426d54d9ecb40930baad985cbd02a1Kay Sievers
1328f66ad16b5afeb5684858c27e121a46c1959eKay Sievers return 0;
de892aea1c486b59e04884268b612081d1660514Kay Sievers}
de892aea1c486b59e04884268b612081d1660514Kay Sievers