- fix ip route rejecting dotted quads as prefix
[oweals/busybox.git] / networking / libiproute / utils.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * utils.c
4  *
5  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
6  *
7  * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
8  *
9  * Changes:
10  *
11  * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
12  */
13
14 #include "libbb.h"
15 #include "utils.h"
16 #include "inet_common.h"
17
18 int get_integer(int *val, char *arg, int base)
19 {
20         long res;
21         char *ptr;
22
23         if (!arg || !*arg)
24                 return -1;
25         res = strtol(arg, &ptr, base);
26         if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN)
27                 return -1;
28         *val = res;
29         return 0;
30 }
31 //XXX: FIXME: use some libbb function instead
32 int get_unsigned(unsigned *val, char *arg, int base)
33 {
34         unsigned long res;
35         char *ptr;
36
37         if (!arg || !*arg)
38                 return -1;
39         res = strtoul(arg, &ptr, base);
40         if (!ptr || ptr == arg || *ptr || res > UINT_MAX)
41                 return -1;
42         *val = res;
43         return 0;
44 }
45
46 int get_u32(uint32_t * val, char *arg, int base)
47 {
48         unsigned long res;
49         char *ptr;
50
51         if (!arg || !*arg)
52                 return -1;
53         res = strtoul(arg, &ptr, base);
54         if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL)
55                 return -1;
56         *val = res;
57         return 0;
58 }
59
60 int get_u16(uint16_t * val, char *arg, int base)
61 {
62         unsigned long res;
63         char *ptr;
64
65         if (!arg || !*arg)
66                 return -1;
67         res = strtoul(arg, &ptr, base);
68         if (!ptr || ptr == arg || *ptr || res > 0xFFFF)
69                 return -1;
70         *val = res;
71         return 0;
72 }
73
74 int get_u8(uint8_t * val, char *arg, int base)
75 {
76         unsigned long res;
77         char *ptr;
78
79         if (!arg || !*arg)
80                 return -1;
81         res = strtoul(arg, &ptr, base);
82         if (!ptr || ptr == arg || *ptr || res > 0xFF)
83                 return -1;
84         *val = res;
85         return 0;
86 }
87
88 int get_s16(int16_t * val, char *arg, int base)
89 {
90         long res;
91         char *ptr;
92
93         if (!arg || !*arg)
94                 return -1;
95         res = strtol(arg, &ptr, base);
96         if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000)
97                 return -1;
98         *val = res;
99         return 0;
100 }
101
102 int get_s8(int8_t * val, char *arg, int base)
103 {
104         long res;
105         char *ptr;
106
107         if (!arg || !*arg)
108                 return -1;
109         res = strtol(arg, &ptr, base);
110         if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80)
111                 return -1;
112         *val = res;
113         return 0;
114 }
115
116 int get_addr_1(inet_prefix * addr, char *name, int family)
117 {
118         memset(addr, 0, sizeof(*addr));
119
120         if (strcmp(name, bb_str_default) == 0 ||
121                 strcmp(name, "all") == 0 || strcmp(name, "any") == 0) {
122                 addr->family = family;
123                 addr->bytelen = (family == AF_INET6 ? 16 : 4);
124                 addr->bitlen = -1;
125                 return 0;
126         }
127
128         if (strchr(name, ':')) {
129                 addr->family = AF_INET6;
130                 if (family != AF_UNSPEC && family != AF_INET6)
131                         return -1;
132                 if (inet_pton(AF_INET6, name, addr->data) <= 0)
133                         return -1;
134                 addr->bytelen = 16;
135                 addr->bitlen = -1;
136                 return 0;
137         }
138
139         addr->family = AF_INET;
140         if (family != AF_UNSPEC && family != AF_INET)
141                 return -1;
142         if (inet_pton(AF_INET, name, addr->data) <= 0)
143                 return -1;
144         addr->bytelen = 4;
145         addr->bitlen = -1;
146         return 0;
147 }
148
149 int get_prefix_1(inet_prefix * dst, char *arg, int family)
150 {
151         int err;
152         unsigned plen;
153         char *slash;
154
155         memset(dst, 0, sizeof(*dst));
156
157         if (strcmp(arg, bb_str_default) == 0 || strcmp(arg, "any") == 0) {
158                 dst->family = family;
159                 dst->bytelen = 0;
160                 dst->bitlen = 0;
161                 return 0;
162         }
163
164         slash = strchr(arg, '/');
165         if (slash)
166                 *slash = '\0';
167         err = get_addr_1(dst, arg, family);
168         if (err == 0) {
169                 dst->bitlen = (dst->family == AF_INET6) ? 128 : 32;
170                 if (slash) {
171                         inet_prefix netmask_pfx;
172
173                         netmask_pfx.family = AF_UNSPEC;
174                         if ((get_unsigned(&plen, slash + 1, 0) || plen > dst->bitlen)
175                                 && (get_addr_1(&netmask_pfx, slash + 1, family)))
176                                 err = -1;
177                         else if (netmask_pfx.family == AF_INET) {
178                                 /* fill in prefix length of dotted quad */
179                                 uint32_t mask = ntohl(netmask_pfx.data[0]);
180                                 uint32_t host = ~mask;
181
182                                 /* a valid netmask must be 2^n - 1 */
183                                 if (!(host & (host + 1))) {
184                                         for (plen = 0; mask; mask <<= 1)
185                                                 ++plen;
186                                         if (plen >= 0 && plen <= dst->bitlen) {
187                                                         dst->bitlen = plen;
188                                                         /* dst->flags |= PREFIXLEN_SPECIFIED; */
189                                         } else
190                                                 err = -1;
191                                 } else
192                                         err = -1;
193                         } else {
194                                 /* plain prefix */
195                                 dst->bitlen = plen;
196                         }
197                 }
198         }
199         if (slash)
200                 *slash = '/';
201         return err;
202 }
203
204 int get_addr(inet_prefix * dst, char *arg, int family)
205 {
206         if (family == AF_PACKET) {
207                 bb_error_msg_and_die("\"%s\" may be inet %s, but it is not allowed in this context", arg, "address");
208         }
209         if (get_addr_1(dst, arg, family)) {
210                 bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "address", arg);
211         }
212         return 0;
213 }
214
215 int get_prefix(inet_prefix * dst, char *arg, int family)
216 {
217         if (family == AF_PACKET) {
218                 bb_error_msg_and_die("\"%s\" may be inet %s, but it is not allowed in this context", arg, "prefix");
219         }
220         if (get_prefix_1(dst, arg, family)) {
221                 bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "prefix", arg);
222         }
223         return 0;
224 }
225
226 uint32_t get_addr32(char *name)
227 {
228         inet_prefix addr;
229
230         if (get_addr_1(&addr, name, AF_INET)) {
231                 bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "IP", "address", name);
232         }
233         return addr.data[0];
234 }
235
236 void incomplete_command(void)
237 {
238         bb_error_msg_and_die("command line is not complete, try option \"help\"");
239 }
240
241 void invarg(const char *arg, const char *opt)
242 {
243         bb_error_msg_and_die(bb_msg_invalid_arg, arg, opt);
244 }
245
246 void duparg(const char *key, const char *arg)
247 {
248         bb_error_msg_and_die("duplicate \"%s\": \"%s\" is the second value", key, arg);
249 }
250
251 void duparg2(const char *key, const char *arg)
252 {
253         bb_error_msg_and_die("either \"%s\" is duplicate, or \"%s\" is garbage", key, arg);
254 }
255
256 int inet_addr_match(inet_prefix * a, inet_prefix * b, int bits)
257 {
258         uint32_t *a1 = a->data;
259         uint32_t *a2 = b->data;
260         int words = bits >> 0x05;
261
262         bits &= 0x1f;
263
264         if (words)
265                 if (memcmp(a1, a2, words << 2))
266                         return -1;
267
268         if (bits) {
269                 uint32_t w1, w2;
270                 uint32_t mask;
271
272                 w1 = a1[words];
273                 w2 = a2[words];
274
275                 mask = htonl((0xffffffff) << (0x20 - bits));
276
277                 if ((w1 ^ w2) & mask)
278                         return 1;
279         }
280
281         return 0;
282 }
283
284 const char *rt_addr_n2a(int af, int UNUSED_PARAM len,
285                 void *addr, char *buf, int buflen)
286 {
287         switch (af) {
288         case AF_INET:
289         case AF_INET6:
290                 return inet_ntop(af, addr, buf, buflen);
291         default:
292                 return "???";
293         }
294 }
295
296
297 const char *format_host(int af, int len, void *addr, char *buf, int buflen)
298 {
299 #ifdef RESOLVE_HOSTNAMES
300         if (resolve_hosts) {
301                 struct hostent *h_ent;
302
303                 if (len <= 0) {
304                         switch (af) {
305                         case AF_INET:
306                                 len = 4;
307                                 break;
308                         case AF_INET6:
309                                 len = 16;
310                                 break;
311                         default:;
312                         }
313                 }
314                 if (len > 0) {
315                         h_ent = gethostbyaddr(addr, len, af);
316                         if (h_ent != NULL) {
317                                 safe_strncpy(buf, h_ent->h_name, buflen);
318                                 return buf;
319                         }
320                 }
321         }
322 #endif
323         return rt_addr_n2a(af, len, addr, buf, buflen);
324 }