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
|
/*
* convert from text form of subnet specification to binary
*
* Copyright (C) 2000 Henry Spencer.
* Copyright (C) 2019 Andrew Cagney <cagney@gnu.org>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <https://www.gnu.org/licenses/lgpl-2.1.txt>.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
* License for more details.
*/
#include <string.h>
#include "ip_subnet.h"
#include "ip_info.h" /* ipv6_info */
#include "lswlog.h" /* for dbg() */
#include "ip_protocol.h"
#ifndef DEFAULTSUBNET
#define DEFAULTSUBNET "%default"
#endif
/*
* ttosubnet - convert text "addr/mask" to address and mask
* Mask can be integer bit count.
*/
err_t ttosubnet_num(shunk_t src, const struct ip_info *afi, /* could be NULL */
ip_subnet *dst, ip_address *nonzero_host)
{
*dst = unset_subnet;
*nonzero_host = unset_address;
err_t oops;
/*
* Match %default, can't work when AFI=NULL.
*
* you cannot use af==AF_UNSPEC and src=0/0,
* makes no sense as will it be AF_INET
*/
if (hunk_strcaseeq(src, DEFAULTSUBNET)) {
if (afi == NULL) {
return "unknown address family with " DEFAULTSUBNET " subnet not allowed.";
}
*dst = afi->subnet.all; /* 0.0.0.0/0 or ::/0 */
return NULL;
}
/* split the input into ADDR [ "/" MASK ] */
char slash = '\0';
shunk_t addr = shunk_token(&src, &slash, "/");
shunk_t mask = src;
/* parse ADDR */
ip_address address;
oops = ttoaddress_num(addr, afi, &address);
if (oops != NULL) {
return oops;
}
if (afi == NULL) {
afi = address_info(address);
}
passert(afi != NULL);
/* parse [ "/" MASK ] */
uintmax_t prefix_len = afi->mask_cnt;
if (slash == '/') {
/* eat entire MASK */
oops = shunk_to_uintmax(mask, NULL, 10, &prefix_len);
if (oops != NULL) {
if (afi == &ipv4_info) {
/*1.2.3.0/255.255.255.0?*/
ip_address masktmp;
oops = ttoaddress_num(mask, afi, &masktmp);
if (oops != NULL) {
return oops;
}
int i = ip_bytes_mask_len(afi, masktmp.bytes);
if (i < 0) {
return "non-contiguous or otherwise erroneous mask";
}
prefix_len = i;
} else {
return "masks are not permitted for IPv6 addresses";
}
} else if (prefix_len > afi->mask_cnt) {
return "mask is too big";
}
}
/* check host-part is zero */
struct ip_bytes routing_prefix = ip_bytes_blit(afi, address.bytes,
&keep_routing_prefix,
&clear_host_identifier,
prefix_len);
if (ip_bytes_cmp(afi->ip_version, routing_prefix,
afi->ip_version, address.bytes) != 0) {
*nonzero_host = address;
}
*dst = subnet_from_raw(HERE, afi, routing_prefix, prefix_len);
return NULL;
}
|