aboutsummaryrefslogtreecommitdiff
path: root/networking/udhcp/d6_socket.c
blob: 8ddee5a8ee6278374ec11bdf5e4481b666e0d203 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* vi: set sw=4 ts=4: */
/*
 * Copyright (C) 2011 Denys Vlasenko.
 *
 * Licensed under GPLv2, see file LICENSE in this source tree.
 */
#include "common.h"
#include "d6_common.h"
#include <net/if.h>
#include <ifaddrs.h>
#include <netpacket/packet.h>

int FAST_FUNC d6_read_interface(
		const char *interface,
		int *ifindex,
		struct in6_addr *nip6,
		uint8_t *mac)
{
	int retval = 3;
	struct ifaddrs *ifap, *ifa;

	getifaddrs(&ifap);
	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
		struct sockaddr_in6 *sip6;

		if (!ifa->ifa_addr || (strcmp(ifa->ifa_name, interface) != 0))
			continue;

		if (ifa->ifa_addr->sa_family == AF_PACKET) {
			struct sockaddr_ll *sll = (void*)(ifa->ifa_addr);
			memcpy(mac, sll->sll_addr, 6);
			log2("MAC %02x:%02x:%02x:%02x:%02x:%02x",
				mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
			);
			*ifindex = sll->sll_ifindex;
			log2("ifindex %d", *ifindex);
			retval &= (3 - (1<<0));
		}
#if 0
		if (ifa->ifa_addr->sa_family == AF_INET) {
			*nip = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
			log1("IP %s", inet_ntoa(((struct sockaddr_in *)ifa->ifa_addr)->sin_addr));
		}
#endif
/* RFC 3315
 * 16. Client Source Address and Interface Selection
 *
 * "When a client sends a DHCP message to the
 * All_DHCP_Relay_Agents_and_Servers address, ... ... The client
 * MUST use a link-local address assigned to the interface for which it
 * is requesting configuration information as the source address in the
 * header of the IP datagram."
 */
		sip6 = (void*)(ifa->ifa_addr);

		if (ifa->ifa_addr->sa_family == AF_INET6
		 && IN6_IS_ADDR_LINKLOCAL(&sip6->sin6_addr)
		) {
			*nip6 = sip6->sin6_addr; /* struct copy */
			log1(
				"IPv6 %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
				nip6->s6_addr[0], nip6->s6_addr[1],
				nip6->s6_addr[2], nip6->s6_addr[3],
				nip6->s6_addr[4], nip6->s6_addr[5],
				nip6->s6_addr[6], nip6->s6_addr[7],
				nip6->s6_addr[8], nip6->s6_addr[9],
				nip6->s6_addr[10], nip6->s6_addr[11],
				nip6->s6_addr[12], nip6->s6_addr[13],
				nip6->s6_addr[14], nip6->s6_addr[15]
			);
			retval &= (3 - (1<<1));
		}
	}
	freeifaddrs(ifap);

	if (retval & (1<<0)) {
		/* This iface has no MAC (e.g. ppp), generate a random one */
		struct ifreq ifr;
		int fd;

		/*memset(&ifr, 0, sizeof(ifr)); - SIOCGIFINDEX does not need to clear all */
		strncpy_IFNAMSIZ(ifr.ifr_name, interface);
		fd = xsocket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
		if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) {
			*ifindex = ifr.ifr_ifindex;
			log2("ifindex %d", *ifindex);
			if (((uint32_t*)mac)[0] == 0) {
				/* invent a fictitious MAC (once) */
				((uint32_t*)mac)[0] = rand();
				((uint16_t*)mac)[2] = rand();
				mac[0] &= 0xfc; /* make sure it's not bcast */
			}
			retval &= (3 - (1<<0));
		}
		close(fd);
	}

	if (retval == 0)
		return retval;

	if (retval & (1<<0))
		bb_error_msg("can't get %s", "MAC");
	if (retval & (1<<1))
		bb_error_msg("can't get %s", "link-local IPv6 address");
	return retval;
}

int FAST_FUNC d6_listen_socket(int port, const char *inf)
{
	int fd;
	struct sockaddr_in6 addr;

	log1("opening listen socket on *:%d %s", port, inf);
	fd = xsocket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);

	setsockopt_reuseaddr(fd);
	if (setsockopt_broadcast(fd) == -1)
		bb_simple_perror_msg_and_die("SO_BROADCAST");

	/* NB: bug 1032 says this doesn't work on ethernet aliases (ethN:M) */
	if (setsockopt_bindtodevice(fd, inf))
		xfunc_die(); /* warning is already printed */

	memset(&addr, 0, sizeof(addr));
	addr.sin6_family = AF_INET6;
	addr.sin6_port = htons(port);
	/* addr.sin6_addr is all-zeros */
	xbind(fd, (struct sockaddr *)&addr, sizeof(addr));

	return fd;
}