util.c revision 1.1
1/* $OpenBSD: util.c,v 1.1 2015/11/04 09:45:52 mpi Exp $ */ 2 3/* 4 * Copyright (c) 2015 Martin Pieuchot 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/socket.h> 20#include <sys/domain.h> 21#include <sys/queue.h> 22#include <net/rtable.h> 23#include <net/route.h> 24 25#include <netinet/in.h> 26#include <arpa/inet.h> 27 28#include <assert.h> 29#include <err.h> 30#include <stddef.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34 35#include "util.h" 36 37struct domain inetdomain = { 38 AF_INET, "inet", NULL, NULL, NULL, NULL, NULL, 39 sizeof(struct sockaddr_in), offsetof(struct sockaddr_in, sin_addr), 40}; 41 42struct domain inet6domain = { 43 AF_INET6, "inet6", NULL, NULL, NULL, NULL, NULL, 44 sizeof(struct sockaddr_in6), offsetof(struct sockaddr_in6, sin6_addr), 45}; 46 47struct domain *domains[] = { &inetdomain, &inet6domain, NULL }; 48 49int sa2plen(sa_family_t, struct sockaddr *); 50 51/* 52 * Insert a route from a string containing a destination: "192.168.1/24" 53 */ 54void 55route_insert(unsigned int rid, sa_family_t af, char *string) 56{ 57 struct sockaddr_storage ss, ms; 58 struct sockaddr *ndst, *dst = (struct sockaddr *)&ss; 59 struct sockaddr *mask = (struct sockaddr *)&ms; 60 struct rtentry *rt, *nrt; 61 char ip[INET6_ADDRSTRLEN]; 62 int plen; 63 64 rt = calloc(1, sizeof(*rt)); 65 if (rt == NULL) 66 errx(1, "out of memory"); 67 68 plen = inet_net_ptosa(af, string, dst, mask); 69 if (plen == -1) 70 err(1, "wrong line: %s", string); 71 72 /* Normalize sockaddr a la rtrequest1(9) */ 73 ndst = malloc(dst->sa_len); 74 if (ndst == NULL) 75 errx(1, "out of memory"); 76 rt_maskedcopy(dst, ndst, mask); 77 78 if (rtable_insert(rid, ndst, mask, NULL, 0, rt)) { 79 inet_net_satop(af, rt_key(rt), plen, ip, sizeof(ip)); 80 errx(1, "can't add route: %s\n", ip); 81 } 82 nrt = rtable_lookup(rid, dst, mask, NULL, RTP_ANY); 83 if (nrt != rt) { 84 inet_net_satop(af, rt_key(rt), plen, ip, sizeof(ip)); 85 errx(1, "added route not found: %s\n", ip); 86 } 87} 88 89/* 90 * Delete a route from a string containing a destination: "192.168.1/24" 91 */ 92void 93route_delete(unsigned int rid, sa_family_t af, char *string) 94{ 95 struct sockaddr_storage ss, ms; 96 struct sockaddr *dst = (struct sockaddr *)&ss; 97 struct sockaddr *mask = (struct sockaddr *)&ms; 98 struct rtentry *rt, *nrt; 99 char ip[INET6_ADDRSTRLEN]; 100 int plen; 101 102 plen = inet_net_ptosa(af, string, dst, mask); 103 if (plen == -1) 104 err(1, "wrong line: %s", string); 105 106 rt = rtable_lookup(0, dst, mask, NULL, RTP_ANY); 107 if (rt == NULL) { 108 inet_net_satop(af, dst, plen, ip, sizeof(ip)); 109 errx(1, "can't find route: %s\n", ip); 110 } 111 112 assert(memcmp(rt_key(rt), dst, dst->sa_len) == 0); 113 assert(maskcmp(af, rt_mask(rt), mask) == 0); 114 115 if (rtable_delete(0, dst, mask, 0, rt)) { 116 inet_net_satop(af, dst, plen, ip, sizeof(ip)); 117 errx(1, "can't rm route: %s\n", ip); 118 } 119 120 nrt = rtable_lookup(0, dst, mask, NULL, RTP_ANY); 121 if (nrt != NULL) { 122 char ip0[INET6_ADDRSTRLEN]; 123 inet_net_satop(af, rt_key(nrt), plen, ip, sizeof(ip)); 124 inet_net_satop(af, rt_key(rt), plen, ip0, sizeof(ip0)); 125 errx(1, "found: %s after deleting: %s", ip, ip0); 126 } 127 128 free(rt_key(rt)); 129 free(rt); 130} 131 132/* 133 * Lookup a route from a string containing a destination: "192.168.1/24" 134 */ 135void 136route_lookup(unsigned int rid, sa_family_t af, char *string) 137{ 138 struct sockaddr_storage ss, ms; 139 struct sockaddr *dst = (struct sockaddr *)&ss; 140 struct sockaddr *mask = (struct sockaddr *)&ms; 141 struct rtentry *rt; 142 char ip[INET6_ADDRSTRLEN]; 143 int plen; 144 145 plen = inet_net_ptosa(af, string, dst, mask); 146 if (plen == -1) 147 err(1, "wrong line: %s", string); 148 149 rt = rtable_lookup(0, dst, mask, NULL, RTP_ANY); 150 if (rt == NULL) { 151 inet_net_satop(af, dst, plen, ip, sizeof(ip)); 152 errx(1, "%s not found\n", ip); 153 } 154 assert(memcmp(rt_key(rt), dst, dst->sa_len) == 0); 155 assert(maskcmp(af, rt_mask(rt), mask) == 0); 156} 157 158int 159do_from_file(unsigned int rid, sa_family_t af, char *filename, 160 void (*func)(unsigned int, sa_family_t, char *)) 161{ 162 FILE *fp; 163 char *buf; 164 size_t len; 165 int lines = 0; 166 167 if ((fp = fopen(filename, "r")) == NULL) 168 errx(1, "No such file: %s\n", filename); 169 170 while ((buf = fgetln(fp, &len)) != NULL) { 171 if (buf[len - 1] == '\n') 172 buf[len - 1] = '\0'; 173 174 (*func)(rid, af, buf); 175 lines++; 176 } 177 fclose(fp); 178 179 return (lines); 180} 181 182int 183rtentry_dump(struct rtentry *rt, void *w, unsigned int rid) 184{ 185 char dest[INET6_ADDRSTRLEN]; 186 int plen; 187 sa_family_t af = rt_key(rt)->sa_family; 188 189 plen = sa2plen(af, rt_mask(rt)); 190 inet_net_satop(af, rt_key(rt), plen, dest, sizeof(dest)); 191 printf("%s\n", dest); 192 193 return (0); 194} 195 196int 197rtentry_delete(struct rtentry *rt, void *w, unsigned int rid) 198{ 199 char dest[INET6_ADDRSTRLEN]; 200 int plen; 201 sa_family_t af = rt_key(rt)->sa_family; 202 203 plen = sa2plen(af, rt_mask(rt)); 204 205 if (rtable_delete(0, rt_key(rt), rt_mask(rt), 0, rt)) { 206 inet_net_satop(af, rt_key(rt), plen, dest, sizeof(dest)); 207 errx(1, "can't rm route: %s\n", dest); 208 } 209 return (0); 210} 211 212void 213rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst, 214 struct sockaddr *netmask) 215{ 216 uint8_t *cp1 = (uint8_t *)src; 217 uint8_t *cp2 = (uint8_t *)dst; 218 uint8_t *cp3 = (uint8_t *)netmask; 219 uint8_t *cplim = cp2 + *cp3; 220 uint8_t *cplim2 = cp2 + *cp1; 221 222 *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ 223 cp3 += 2; 224 if (cplim > cplim2) 225 cplim = cplim2; 226 while (cp2 < cplim) 227 *cp2++ = *cp1++ & *cp3++; 228 if (cp2 < cplim2) 229 memset(cp2, 0, (unsigned int)(cplim2 - cp2)); 230} 231 232int 233sa2plen(sa_family_t af, struct sockaddr *mask) 234{ 235 uint8_t *ap, *ep; 236 int off, plen = 0; 237 238 switch (af) { 239 case AF_INET: 240 off = offsetof(struct sockaddr_in, sin_addr); 241 break; 242 case AF_INET6: 243 off = offsetof(struct sockaddr_in6, sin6_addr); 244 break; 245 default: 246 return (-1); 247 } 248 249 /* Default route */ 250 if (mask->sa_len == 0) 251 return (0); 252 253 ap = (uint8_t *)((uint8_t *)mask) + off; 254 ep = (uint8_t *)((uint8_t *)mask) + mask->sa_len; 255 if (ap > ep) 256 return (-1); 257 258 if (ap == ep) 259 return (0); 260 261 /* "Beauty" adapted from sbin/route/show.c ... */ 262 while (ap < ep) { 263 switch (*ap) { 264 case 0xff: 265 plen += 8; 266 ap++; 267 break; 268 case 0xfe: 269 plen += 7; 270 return (plen); 271 case 0xfc: 272 plen += 6; 273 return (plen); 274 case 0xf8: 275 plen += 5; 276 return (plen); 277 case 0xf0: 278 plen += 4; 279 return (plen); 280 case 0xe0: 281 plen += 3; 282 return (plen); 283 case 0xc0: 284 plen += 2; 285 return (plen); 286 case 0x80: 287 plen += 1; 288 return (plen); 289 case 0x00: 290 return (plen); 291 default: 292 /* Non contiguous mask. */ 293 return (-1); 294 } 295 296 } 297 298 return (plen); 299} 300 301 302in_addr_t 303prefixlen2mask(u_int8_t prefixlen) 304{ 305 if (prefixlen == 0) 306 return (0); 307 308 return (0xffffffff << (32 - prefixlen)); 309} 310 311int 312inet_net_ptosa(sa_family_t af, const char *buf, struct sockaddr *sa, 313 struct sockaddr *ma) 314{ 315 struct sockaddr_in *sin = (struct sockaddr_in *)sa; 316 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 317 int i, plen; 318 319 switch (af) { 320 case AF_INET: 321 memset(sin, 0, sizeof(*sin)); 322 sin->sin_family = af; 323 sin->sin_len = sizeof(*sin); 324 plen = inet_net_pton(af, buf, &sin->sin_addr, 325 sizeof(sin->sin_addr)); 326 if (plen == -1 || ma == NULL) 327 break; 328 329 sin = (struct sockaddr_in *)ma; 330 memset(sin, 0, sizeof(*sin)); 331 sin->sin_len = sizeof(*sin); 332 sin->sin_family = 0; 333 sin->sin_addr.s_addr = htonl(prefixlen2mask(plen)); 334 break; 335 case AF_INET6: 336 memset(sin6, 0, sizeof(*sin6)); 337 sin6->sin6_family = af; 338 sin6->sin6_len = sizeof(*sin6); 339 plen = inet_net_pton(af, buf, &sin6->sin6_addr, 340 sizeof(sin6->sin6_addr)); 341 if (plen == -1 || ma == NULL) 342 break; 343 344 sin6 = (struct sockaddr_in6 *)ma; 345 memset(sin6, 0, sizeof(*sin6)); 346 sin6->sin6_len = sizeof(*sin6); 347 sin6->sin6_family = 0; 348 for (i = 0; i < plen / 8; i++) 349 sin6->sin6_addr.s6_addr[i] = 0xff; 350 i = plen % 8; 351 if (i) 352 sin6->sin6_addr.s6_addr[plen / 8] = 0xff00 >> i; 353 break; 354 default: 355 plen = -1; 356 } 357 358 return (plen); 359} 360 361/* 362 * Only compare the address fields, we cannot use memcmp(3) because 363 * the radix tree abuses the first fields of the mask sockaddr for 364 * a different purpose. 365 */ 366int 367maskcmp(sa_family_t af, struct sockaddr *sa1, struct sockaddr *sa2) 368{ 369 struct sockaddr_in *sin1, *sin2; 370 struct sockaddr_in6 *sin61, *sin62; 371 int len; 372 373 switch (af) { 374 case AF_INET: 375 sin1 = (struct sockaddr_in *)sa1; 376 sin2 = (struct sockaddr_in *)sa2; 377 len = sizeof(sin1->sin_addr); 378 return memcmp(&sin1->sin_addr, &sin2->sin_addr, len); 379 case AF_INET6: 380 sin61 = (struct sockaddr_in6 *)sa1; 381 sin62 = (struct sockaddr_in6 *)sa2; 382 len = sizeof(sin61->sin6_addr); 383 return memcmp(&sin61->sin6_addr, &sin62->sin6_addr, len); 384 default: 385 return (-1); 386 } 387} 388 389char * 390inet_net_satop(sa_family_t af, struct sockaddr *sa, int plen, char *buf, 391 size_t len) 392{ 393 struct sockaddr_in *sin = (struct sockaddr_in *)sa; 394 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 395 396 switch (af) { 397 case AF_INET: 398 return inet_net_ntop(af, &sin->sin_addr, plen, buf, len); 399 case AF_INET6: 400 return inet_net_ntop(af, &sin6->sin6_addr, plen, buf, len); 401 default: 402 return (NULL); 403 } 404} 405