b9bf0aa094ade19926eaadee5e6f31eaee089f01
2 * Copyright (C) 2012-2013 Steven Barth <steven@midlink.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License v2 as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
22 #include <arpa/inet.h>
23 #include <netinet/in.h>
27 static const char hexdigits
[] = "0123456789abcdef";
28 static const int8_t hexvals
[] = {
29 -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -1, -1, -2, -1, -1,
30 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
31 -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
32 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
33 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
34 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
35 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
36 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
41 static char *argv
[4] = {NULL
, NULL
, NULL
, NULL
};
42 static volatile char *delayed_call
= NULL
;
45 int script_init(const char *path
, const char *ifname
)
47 argv
[0] = (char*)path
;
48 argv
[1] = (char*)ifname
;
53 ssize_t
script_unhexlify(uint8_t *dst
, size_t len
, const char *src
)
56 for (c
= 0; c
< len
&& src
[0] && src
[1]; ++c
) {
57 int8_t x
= (int8_t)*src
++;
58 int8_t y
= (int8_t)*src
++;
59 if (x
< 0 || (x
= hexvals
[x
]) < 0
60 || y
< 0 || (y
= hexvals
[y
]) < 0)
63 while (((int8_t)*src
) < 0 ||
64 (*src
&& hexvals
[(uint8_t)*src
] < 0))
72 static void script_hexlify(char *dst
, const uint8_t *src
, size_t len
) {
73 for (size_t i
= 0; i
< len
; ++i
) {
74 *dst
++ = hexdigits
[src
[i
] >> 4];
75 *dst
++ = hexdigits
[src
[i
] & 0x0f];
81 static void ipv6_to_env(const char *name
,
82 const struct in6_addr
*addr
, size_t cnt
)
84 size_t buf_len
= strlen(name
);
85 char *buf
= realloc(NULL
, cnt
* INET6_ADDRSTRLEN
+ buf_len
+ 2);
86 memcpy(buf
, name
, buf_len
);
88 for (size_t i
= 0; i
< cnt
; ++i
) {
89 inet_ntop(AF_INET6
, &addr
[i
], &buf
[buf_len
], INET6_ADDRSTRLEN
);
90 buf_len
+= strlen(&buf
[buf_len
]);
93 buf
[buf_len
- 1] = '\0';
98 static void fqdn_to_env(const char *name
, const uint8_t *fqdn
, size_t len
)
100 size_t buf_len
= strlen(name
);
101 size_t buf_size
= len
+ buf_len
+ 2;
102 const uint8_t *fqdn_end
= fqdn
+ len
;
103 char *buf
= realloc(NULL
, len
+ buf_len
+ 2);
104 memcpy(buf
, name
, buf_len
);
105 buf
[buf_len
++] = '=';
107 while (l
> 0 && fqdn
< fqdn_end
) {
108 l
= dn_expand(fqdn
, fqdn_end
, fqdn
, &buf
[buf_len
], buf_size
- buf_len
);
110 buf_len
+= strlen(&buf
[buf_len
]);
111 buf
[buf_len
++] = ' ';
113 buf
[buf_len
- 1] = '\0';
118 static void bin_to_env(uint8_t *opts
, size_t len
)
120 uint8_t *oend
= opts
+ len
, *odata
;
121 uint16_t otype
, olen
;
122 dhcpv6_for_each_option(opts
, oend
, otype
, olen
, odata
) {
123 char *buf
= realloc(NULL
, 14 + (olen
* 2));
126 snprintf(buf
, 14, "OPTION_%hu=", otype
);
127 buf_len
+= strlen(buf
);
129 script_hexlify(&buf
[buf_len
], odata
, olen
);
141 static void entry_to_env(const char *name
, const void *data
, size_t len
, enum entry_type type
)
143 size_t buf_len
= strlen(name
);
144 const struct odhcp6c_entry
*e
= data
;
145 char *buf
= realloc(NULL
, buf_len
+ 2 + (len
/ sizeof(*e
)) * 144);
146 memcpy(buf
, name
, buf_len
);
147 buf
[buf_len
++] = '=';
149 for (size_t i
= 0; i
< len
/ sizeof(*e
); ++i
) {
150 inet_ntop(AF_INET6
, &e
[i
].target
, &buf
[buf_len
], INET6_ADDRSTRLEN
);
151 buf_len
+= strlen(&buf
[buf_len
]);
152 if (type
!= ENTRY_HOST
) {
153 buf_len
+= snprintf(&buf
[buf_len
], 6, "/%hhu", e
[i
].length
);
154 if (type
== ENTRY_ROUTE
) {
155 buf
[buf_len
++] = ',';
156 if (!IN6_IS_ADDR_UNSPECIFIED(&e
[i
].router
)) {
157 inet_ntop(AF_INET6
, &e
[i
].router
, &buf
[buf_len
], INET6_ADDRSTRLEN
);
158 buf_len
+= strlen(&buf
[buf_len
]);
160 buf_len
+= snprintf(&buf
[buf_len
], 24, ",%u", e
[i
].valid
);
161 buf_len
+= snprintf(&buf
[buf_len
], 12, ",%u", e
[i
].priority
);
163 buf_len
+= snprintf(&buf
[buf_len
], 24, ",%u,%u", e
[i
].preferred
, e
[i
].valid
);
166 if (type
== ENTRY_PREFIX
&& e
[i
].priority
) {
167 // priority and router are abused for prefix exclusion
168 buf_len
+= snprintf(&buf
[buf_len
], 12, ",excluded=");
169 inet_ntop(AF_INET6
, &e
[i
].router
, &buf
[buf_len
], INET6_ADDRSTRLEN
);
170 buf_len
+= strlen(&buf
[buf_len
]);
171 buf_len
+= snprintf(&buf
[buf_len
], 24, "/%u", e
[i
].priority
);
174 buf
[buf_len
++] = ' ';
177 buf
[buf_len
- 1] = '\0';
182 static void script_call_delayed(int signal
__attribute__((unused
)))
185 script_call((char*)delayed_call
);
189 void script_delay_call(const char *status
, int timeout
)
192 delayed_call
= strdup(status
);
193 signal(SIGALRM
, script_call_delayed
);
199 void script_call(const char *status
)
201 size_t dns_len
, search_len
, custom_len
, sntp_ip_len
, sntp_dns_len
;
202 size_t sip_ip_len
, sip_fqdn_len
, aftr_name_len
;
208 struct in6_addr
*dns
= odhcp6c_get_state(STATE_DNS
, &dns_len
);
209 uint8_t *search
= odhcp6c_get_state(STATE_SEARCH
, &search_len
);
210 uint8_t *custom
= odhcp6c_get_state(STATE_CUSTOM_OPTS
, &custom_len
);
211 struct in6_addr
*sntp
= odhcp6c_get_state(STATE_SNTP_IP
, &sntp_ip_len
);
212 uint8_t *sntp_dns
= odhcp6c_get_state(STATE_SNTP_FQDN
, &sntp_dns_len
);
213 struct in6_addr
*sip
= odhcp6c_get_state(STATE_SIP_IP
, &sip_ip_len
);
214 uint8_t *sip_fqdn
= odhcp6c_get_state(STATE_SIP_FQDN
, &sip_fqdn_len
);
215 uint8_t *aftr_name
= odhcp6c_get_state(STATE_AFTR_NAME
, &aftr_name_len
);
217 size_t prefix_len
, address_len
, ra_pref_len
, ra_route_len
, ra_dns_len
;
218 uint8_t *prefix
= odhcp6c_get_state(STATE_IA_PD
, &prefix_len
);
219 uint8_t *address
= odhcp6c_get_state(STATE_IA_NA
, &address_len
);
220 uint8_t *ra_pref
= odhcp6c_get_state(STATE_RA_PREFIX
, &ra_pref_len
);
221 uint8_t *ra_route
= odhcp6c_get_state(STATE_RA_ROUTE
, &ra_route_len
);
222 uint8_t *ra_dns
= odhcp6c_get_state(STATE_RA_DNS
, &ra_dns_len
);
224 // Don't set environment before forking, because env is leaky.
226 ipv6_to_env("RDNSS", dns
, dns_len
/ sizeof(*dns
));
227 ipv6_to_env("SNTP_IP", sntp
, sntp_ip_len
/ sizeof(*sntp
));
228 ipv6_to_env("SIP_IP", sip
, sip_ip_len
/ sizeof(*sip
));
229 fqdn_to_env("DOMAINS", search
, search_len
);
230 fqdn_to_env("SNTP_FQDN", sntp_dns
, sntp_dns_len
);
231 fqdn_to_env("SIP_DOMAIN", sip_fqdn
, sip_fqdn_len
);
232 fqdn_to_env("AFTR", aftr_name
, aftr_name_len
);
233 bin_to_env(custom
, custom_len
);
234 entry_to_env("PREFIXES", prefix
, prefix_len
, ENTRY_PREFIX
);
235 entry_to_env("ADDRESSES", address
, address_len
, ENTRY_ADDRESS
);
236 entry_to_env("RA_ADDRESSES", ra_pref
, ra_pref_len
, ENTRY_ADDRESS
);
237 entry_to_env("RA_ROUTES", ra_route
, ra_route_len
, ENTRY_ROUTE
);
238 entry_to_env("RA_DNS", ra_dns
, ra_dns_len
, ENTRY_HOST
);
240 argv
[2] = (char*)status
;
241 execv(argv
[0], argv
);
245 // Delete lost prefixes and user opts
246 odhcp6c_clear_state(STATE_CUSTOM_OPTS
);