summaryrefslogtreecommitdiffstats
path: root/package/busybox/patches/244-udhcpc_add_6rd_option.patch
blob: 18a599640858b209d987d6dc1432d860d479f8ce (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index 0a60261..eaf2b4b 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -56,6 +56,8 @@ const struct dhcp_optflag dhcp_optflags[] = {
 #endif
 	{ OPTION_STATIC_ROUTES                    , 0x79 }, /* DHCP_STATIC_ROUTES */
 	{ OPTION_STATIC_ROUTES                    , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */
+	{ OPTION_6RD                              , 0xd4 }, /* DHCP_6RD (RFC)     */
+	{ OPTION_6RD                              , 0x96 }, /* DHCP_6RD (Comcast) */
 	{ OPTION_STRING                           , 0xfc }, /* DHCP_WPAD          */
 
 	/* Options below have no match in dhcp_option_strings[],
@@ -118,6 +120,8 @@ const char dhcp_option_strings[] ALIGN1 =
 // doesn't work in udhcpd.conf since OPTION_STATIC_ROUTES
 // is not handled yet by "string->option" conversion code:
 	"staticroutes" "\0"/* DHCP_STATIC_ROUTES  */
+	"ip6rd" "\0"       /* DHCP_6RD (RFC)      */
+	"ip6rd" "\0"       /* DHCP_6RD (Comcast)  */
 	"msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */
 	"wpad" "\0"        /* DHCP_WPAD           */
 	;
@@ -146,6 +150,7 @@ const uint8_t dhcp_option_lengths[] ALIGN1 = {
 	[OPTION_S32] =     4,
 	/* Just like OPTION_STRING, we use minimum length here */
 	[OPTION_STATIC_ROUTES] = 5,
+	[OPTION_6RD]           = 22,
 };
 
 
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
index f8f18ff..56cd8b0 100644
--- a/networking/udhcp/common.h
+++ b/networking/udhcp/common.h
@@ -88,6 +88,7 @@ enum {
 	OPTION_S32,
 	OPTION_BIN,
 	OPTION_STATIC_ROUTES,
+	OPTION_6RD,
 #if ENABLE_FEATURE_UDHCP_RFC3397
 	OPTION_DNS_STRING,  /* RFC1035 compressed domain name list */
 	OPTION_SIP_SERVERS,
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index 78aabed..993326f 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -45,6 +45,7 @@ static const uint8_t len_of_option_as_string[] = {
 	[OPTION_IP              ] = sizeof("255.255.255.255 "),
 	[OPTION_IP_PAIR         ] = sizeof("255.255.255.255 ") * 2,
 	[OPTION_STATIC_ROUTES   ] = sizeof("255.255.255.255/32 255.255.255.255 "),
+	[OPTION_6RD             ] = sizeof("32 128 FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF 255.255.255.255 "),
 	[OPTION_STRING          ] = 1,
 #if ENABLE_FEATURE_UDHCP_RFC3397
 	[OPTION_DNS_STRING      ] = 1, /* unused */
@@ -68,6 +69,23 @@ static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
 	return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
 }
 
+static int sprint_nip6(char *dest, const char *pre, const uint8_t *ip)
+{
+	int len = 0;
+	int off;
+	uint16_t word;
+
+	len += sprintf(dest, "%s", pre);
+
+	for (off = 0; off < 16; off += 2)
+	{
+		move_from_unaligned16(word, &ip[off]);
+		len += sprintf(dest+len, "%s%04X", off ? ":" : "", htons(word));
+	}
+
+	return len;
+}
+
 /* really simple implementation, just count the bits */
 static int mton(uint32_t mask)
 {
@@ -177,6 +195,70 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
 
 			return ret;
 		}
+		case OPTION_6RD: {
+			/* Option binary format:
+			 *  0                   1                   2                   3
+			 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+			 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+			 *  |  OPTION_6RD   | option-length |  IPv4MaskLen  |  6rdPrefixLen |
+			 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+			 *  |                                                               |
+			 *  |                           6rdPrefix                           |
+			 *  |                          (16 octets)                          |
+			 *  |                                                               |
+			 *  |                                                               |
+			 *  |                                                               |
+			 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+			 *  |                     6rdBRIPv4Address(es)                      |
+			 *  .                                                               .
+			 *  .                                                               .
+			 *  .                                                               .
+			 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+			 *
+			 * We convert it to a string "IPv4MaskLen 6rdPrefixLen 6rdPrefix 6rdBRIPv4Address"
+			 */
+
+			/* Sanity check: ensure that our length is at least 22 bytes, that
+			 * IPv4MaskLen is <= 32, 6rdPrefixLen <= 128 and that the sum of
+			 * (32 - IPv4MaskLen) + 6rdPrefixLen is less than or equal to 128.
+			 * If any of these requirements is not fulfilled, return with empty
+			 * value.
+			 */
+			if ((len >= 22) && (*option <= 32) && (*(option+1) <= 128) &&
+			    (((32 - *option) + *(option+1)) <= 128))
+			{
+				/* IPv4MaskLen */
+				dest += sprintf(dest, "%u ", *option++);
+				len--;
+
+				/* 6rdPrefixLen */
+				dest += sprintf(dest, "%u ", *option++);
+				len--;
+
+				/* 6rdPrefix */
+				dest += sprint_nip6(dest, "", option);
+				option += 16;
+				len -= 16;
+
+				/* 6rdBRIPv4Addresses */
+				while (len >= 4)
+				{
+					dest += sprint_nip(dest, " ", option);
+					option += 4;
+					len -= 4;
+
+					/* the code to determine the option size fails to work with
+					 * lengths that are not a multiple of the minimum length,
+					 * adding all advertised 6rdBRIPv4Addresses here would
+					 * overflow the destination buffer, therefore skip the rest
+					 * for now
+					 */
+					break;
+				}
+			}
+
+			return ret;
+		}
 #if ENABLE_FEATURE_UDHCP_RFC3397
 		case OPTION_DNS_STRING:
 			/* unpack option into dest; use ret for prefix (i.e., "optname=") */