summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2014-05-08 13:40:41 +0000
committerRobert Mustacchi <rm@joyent.com>2014-05-14 23:19:27 +0000
commit71f4a70d7ca4c7143accc42525bcec0f4da7f4d0 (patch)
treed6595307655c9257e04f8ba4a94f6c0ead2238fd
parent74d5b5a7ae8758330255bfe28ab2399ad19fdd0e (diff)
downloadillumos-joyent-71f4a70d7ca4c7143accc42525bcec0f4da7f4d0.tar.gz
OS-2993 Need IPv6 antispoof prefixes
-rw-r--r--usr/src/lib/libdladm/common/linkprop.c85
-rw-r--r--usr/src/test/util-tests/runfiles/default.run1
-rw-r--r--usr/src/test/util-tests/tests/Makefile2
-rw-r--r--usr/src/test/util-tests/tests/dladm/Makefile40
-rw-r--r--usr/src/test/util-tests/tests/dladm/allowed-ips.ksh207
-rw-r--r--usr/src/uts/common/io/mac/mac_protect.c39
-rw-r--r--usr/src/uts/common/sys/mac_flow.h2
7 files changed, 317 insertions, 59 deletions
diff --git a/usr/src/lib/libdladm/common/linkprop.c b/usr/src/lib/libdladm/common/linkprop.c
index b89860e1c1..5a6eed57d6 100644
--- a/usr/src/lib/libdladm/common/linkprop.c
+++ b/usr/src/lib/libdladm/common/linkprop.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
#include <stdlib.h>
@@ -2348,33 +2348,6 @@ get_protection(dladm_handle_t handle, prop_desc_t *pdp,
return (DLADM_STATUS_OK);
}
-static uint32_t
-nbits_to_mask(int nbits)
-{
- int i;
- uint_t mask = 0;
-
- for (i = 0; i < nbits; i++) {
- mask >>= 1;
- mask |= 0x80000000;
- }
-
- return (mask);
-}
-
-static uint32_t
-mask_to_nbits(uint32_t mask)
-{
- uint_t nbits = 0;
-
- while (mask != 0) {
- nbits++;
- mask <<= 1;
- }
-
- return (nbits);
-}
-
/* ARGSUSED */
static dladm_status_t
get_allowedips(dladm_handle_t handle, prop_desc_t *pdp,
@@ -2402,20 +2375,19 @@ get_allowedips(dladm_handle_t handle, prop_desc_t *pdp,
for (i = 0; i < p->mp_ipaddrcnt; i++) {
if (p->mp_ipaddrs[i].ip_version == IPV4_VERSION) {
ipaddr_t v4addr;
- uint32_t mask;
v4addr = V4_PART_OF_V6(p->mp_ipaddrs[i].ip_addr);
- mask = p->mp_ipaddrs[i].ip_v4netmask;
(void) dladm_ipv4addr2str(&v4addr, prop_val[i]);
- if (mask != 0) {
- int len = strlen(prop_val[i]);
- (void) sprintf(prop_val[i] + len, "/%d",
- mask_to_nbits(mask));
- }
} else {
(void) dladm_ipv6addr2str(&p->mp_ipaddrs[i].ip_addr,
prop_val[i]);
}
+ if (p->mp_ipaddrs[i].ip_netmask != 0) {
+ int len = strlen(prop_val[i]);
+ (void) sprintf(prop_val[i] + len, "/%d",
+ p->mp_ipaddrs[i].ip_netmask);
+ }
+
}
*val_cnt = p->mp_ipaddrcnt;
return (DLADM_STATUS_OK);
@@ -2464,7 +2436,7 @@ check_single_ip(char *buf, mac_ipaddr_t *addr)
in6_addr_t v6addr;
boolean_t isv4 = B_TRUE;
char *p;
- uint32_t mask;
+ uint32_t mask = 0;
/*
* If the IP address is in CIDR format, parse the bits component
@@ -2473,17 +2445,15 @@ check_single_ip(char *buf, mac_ipaddr_t *addr)
*/
if ((p = strchr(buf, '/')) != NULL) {
char *end = NULL;
- long msk;
*p++ = '\0';
if (!isdigit(*p))
return (DLADM_STATUS_INVALID_IP);
- msk = strtol(p, &end, 10);
+ mask = strtol(p, &end, 10);
if (end != NULL && *end != '\0')
return (DLADM_STATUS_INVALID_IP);
- if (msk > 32 || msk < 1)
+ if (mask > 128|| mask < 1)
return (DLADM_STATUS_INVALID_IP);
- mask = nbits_to_mask((int)msk);
}
status = dladm_str2ipv4addr(buf, &v4addr);
@@ -2502,23 +2472,52 @@ check_single_ip(char *buf, mac_ipaddr_t *addr)
IN6_IPADDR_TO_V4MAPPED(v4addr, &addr->ip_addr);
addr->ip_version = IPV4_VERSION;
if (p != NULL) {
+ uint32_t smask;
+
+ /*
+ * Validate the netmask is in the proper range for v4
+ */
+ if (mask > 32 || mask < 1)
+ return (DLADM_STATUS_INVALID_IP);
+
/*
* We have a CIDR style address, confirm that only the
* network number is set.
*/
- if (htonl(v4addr) & ~mask)
+ smask = 0xFFFFFFFFu << (32 - mask);
+ if (htonl(v4addr) & ~smask)
return (DLADM_STATUS_INVALID_IP);
- addr->ip_v4netmask = mask;
}
+ addr->ip_netmask = mask;
} else {
if (IN6_IS_ADDR_UNSPECIFIED(&v6addr))
return (DLADM_STATUS_INVALID_IP);
- if (p != NULL)
+ if (IN6_IS_ADDR_V4MAPPED_ANY(&v6addr))
return (DLADM_STATUS_INVALID_IP);
+ if (p != NULL) {
+ int i, off, high;
+
+ /*
+ * Note that the address in our buffer is stored in
+ * network byte order.
+ */
+ off = 0;
+ for (i = 3; i >= 0; i--) {
+ high = ffsl(ntohl(v6addr._S6_un._S6_u32[i]));
+ if (high != 0)
+ break;
+ off += 32;
+ }
+ off += high;
+ if (128 - off >= mask)
+ return (DLADM_STATUS_INVALID_IP);
+ }
+
addr->ip_addr = v6addr;
addr->ip_version = IPV6_VERSION;
+ addr->ip_netmask = mask;
}
return (DLADM_STATUS_OK);
}
diff --git a/usr/src/test/util-tests/runfiles/default.run b/usr/src/test/util-tests/runfiles/default.run
index 638ba93ce1..3122bb6640 100644
--- a/usr/src/test/util-tests/runfiles/default.run
+++ b/usr/src/test/util-tests/runfiles/default.run
@@ -23,4 +23,5 @@ post =
outputdir = /var/tmp/test_results
[/opt/util-tests/tests/printf_test]
+[/opt/util-tests/tests/allowed-ips]
diff --git a/usr/src/test/util-tests/tests/Makefile b/usr/src/test/util-tests/tests/Makefile
index 41307ba8e4..86d9e01fc8 100644
--- a/usr/src/test/util-tests/tests/Makefile
+++ b/usr/src/test/util-tests/tests/Makefile
@@ -14,6 +14,6 @@
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
#
-SUBDIRS = printf
+SUBDIRS = dladm printf
include $(SRC)/test/Makefile.com
diff --git a/usr/src/test/util-tests/tests/dladm/Makefile b/usr/src/test/util-tests/tests/dladm/Makefile
new file mode 100644
index 0000000000..df3997656c
--- /dev/null
+++ b/usr/src/test/util-tests/tests/dladm/Makefile
@@ -0,0 +1,40 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2014 Joyent, Inc. All rights reserved.
+#
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+ROOTOPTPKG = $(ROOT)/opt/util-tests/tests
+PROG = allowed-ips
+
+ROOTPROG = $(PROG:%=$(ROOTOPTPKG)/%)
+
+all:
+
+install: $(ROOTPROG)
+
+lint:
+
+clobber: clean
+
+clean:
+
+$(CMDS): $(TESTDIR)
+
+$(ROOTOPTPKG):
+ $(INS.dir)
+
+$(ROOTOPTPKG)/%: %.ksh $(ROOTOPTPKG)
+ $(INS.rename)
diff --git a/usr/src/test/util-tests/tests/dladm/allowed-ips.ksh b/usr/src/test/util-tests/tests/dladm/allowed-ips.ksh
new file mode 100644
index 0000000000..866b8c7966
--- /dev/null
+++ b/usr/src/test/util-tests/tests/dladm/allowed-ips.ksh
@@ -0,0 +1,207 @@
+#!/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2014, Joyent, Inc.
+#
+
+ai_arg0="$(basename $0)"
+ai_stub="teststub$$"
+ai_vnic="testvnic$$"
+
+function fatal
+{
+ typeset msg="$*"
+ [[ -z "$msg" ]] && msg="failed"
+ echo "TEST_FAIL: $vt_arg0: $msg" >&2
+ exit 1
+}
+
+function setup
+{
+ dladm create-etherstub $ai_stub || fatal "failed to create etherstub"
+ dladm create-vnic -l $ai_stub $ai_vnic || fatal "failed to create vnic"
+}
+
+function cleanup
+{
+ dladm delete-vnic $ai_vnic || fatal "failed to remove vnic"
+ dladm delete-etherstub $ai_stub || fatal "failed to remove etherstub"
+}
+
+function runtest
+{
+ dladm set-linkprop -p allowed-ips="$@" $ai_vnic 2>/dev/null
+}
+
+function epass
+{
+ runtest $* || fatal "allowed-ips=$* failed, expected success\n"
+}
+
+function efail
+{
+ runtest $* && fatal "allowed-ips=$* succeeded, expected failure\n"
+}
+
+#
+# Run through all IPv6 prefixes for validity with a token prefix
+#
+function allv6
+{
+ typeset i;
+ for ((i = 1; i <= 128; i++)); do
+ epass "8000::/$i"
+ done
+}
+
+#
+# Run through all of the v6 prefixes except /128 and ensure that they fail for
+# a given IPv6 address because the other bits are set.
+#
+function v6specific
+{
+ typeset i;
+ for ((i = 0; i < 128; i++)); do
+ efail 2600:3c00::f03c:91ff:fe96:a267/$i
+ done
+}
+
+setup
+
+# Basic IPv4 single and multiple IPs
+efail 0.0.0.0
+epass 127.0.0.1
+epass 127.0.0.1,127.0.0.2
+efail 127.0.0.1,127.0.0.1
+epass 10.167.169.23
+epass 11.167.169.23
+epass 12.167.169.23
+epass 10.167.169.23,11.167.169.23,12.167.169.23
+efail 256.1.1.1
+efail 1.256.1.1
+efail 1.1.256.1
+efail 1.1.1.256
+efail 300.300.300.300
+efail 300.300.300.300,1.1.1.1
+efail 1.1.1.1,300.300.300.300
+efail 3.-3.3.-3
+
+# Basic IPv4 prefixes
+efail 0.0.0.0/0
+epass 127.0.0.0/8
+efail 127.0.0.1/8
+epass 128.0.0.0/1
+epass 128.0.0.0/2
+epass 128.0.0.0/3
+epass 128.0.0.0/4
+epass 128.0.0.0/5
+epass 128.0.0.0/6
+epass 128.0.0.0/7
+epass 128.0.0.0/8
+epass 128.0.0.0/9
+epass 128.0.0.0/10
+epass 128.0.0.0/11
+epass 128.0.0.0/12
+epass 128.0.0.0/13
+epass 128.0.0.0/14
+epass 128.0.0.0/15
+epass 128.0.0.0/16
+epass 128.0.0.0/17
+epass 128.0.0.0/18
+epass 128.0.0.0/19
+epass 128.0.0.0/20
+epass 128.0.0.0/21
+epass 128.0.0.0/22
+epass 128.0.0.0/23
+epass 128.0.0.0/24
+epass 128.0.0.0/25
+epass 128.0.0.0/26
+epass 128.0.0.0/27
+epass 128.0.0.0/28
+epass 128.0.0.0/29
+epass 128.0.0.0/30
+epass 128.0.0.0/21
+epass 128.0.0.0/32
+
+efail 128.0.0.1/2
+efail 128.0.0.1/4
+efail 128.0.0.1/8
+efail 128.0.0.1/10
+efail 128.0.0.1/12
+efail 128.0.0.1/14
+efail 128.0.0.1/16
+efail 128.0.0.1/18
+efail 128.0.0.1/20
+efail 128.0.0.1/22
+efail 128.0.0.1/24
+efail 128.0.0.1/28
+efail 128.0.0.1/30
+epass 128.0.0.1/32
+
+epass 10.0.0.0/30
+epass 10.0.0.4/30
+epass 10.0.0.8/30
+epass 10.0.0.12/30
+epass 10.0.0.16/30
+epass 10.0.0.20/30
+
+efail 10.0.0.1/30
+efail 10.0.0.5/30
+efail 10.0.0.9/30
+efail 10.0.0.13/30
+efail 10.0.0.17/30
+efail 10.0.0.21/30
+
+epass 10.99.99.0/24,10.88.88.0/24,10.77.7.0/24
+epass 10.99.99.7/32,10.168.0.0/16
+
+efail 10.99.99.7/33
+efail 10.99.99.7/-1
+efail 10.99.99.7/
+efail 10.99.99.7/0
+
+# Basic IPv6 Addresss
+efail ::
+efail 1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1
+efail dead:beef::gg
+epass ::1
+epass ::127.0.0.1
+epass ::10.0.0.0
+efail ::0.0.0.0
+efail ::ffff:0.0.0.0
+epass ::ffff:10.123.167.169
+epass 2600:3c00::f03c:91ff:fe96:a264
+
+# IPv6 Prefixes
+
+efail ::/128
+efail 2600:3c00::f03c:91ff:fe96:a264/129
+efail 2600:3c00::f03c:91ff:fe96:a264/-1
+efail 2600:3c00::f03c:91ff:fe96:a264/-
+efail 2600:3c00::f03c:91ff:fe96:a264/
+efail ::1/1
+efail ::1/20
+
+allv6
+v6specific
+
+epass 2600:3c00::f03c:91ff:fe96:a264/128
+epass 2600:3c00::/64
+
+efail fe80::8:20ff:fead:3361/10
+efail fe80::1/10
+epass fe80::/15
+epass fe82::/15
+
+cleanup
+printf "TEST PASS: $ai_arg0"
diff --git a/usr/src/uts/common/io/mac/mac_protect.c b/usr/src/uts/common/io/mac/mac_protect.c
index 5016cc2665..3c3266ac1d 100644
--- a/usr/src/uts/common/io/mac/mac_protect.c
+++ b/usr/src/uts/common/io/mac/mac_protect.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
#include <sys/strsun.h>
@@ -1534,18 +1534,19 @@ ipnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *protect,
mac_ipaddr_t *v4addr = &protect->mp_ipaddrs[i];
if (v4addr->ip_version == IPV4_VERSION) {
- if (v4addr->ip_v4netmask != 0) {
- /*
- * Since we have a netmask we know this entry
- * signifies the entire subnet. Check if the
- * given address is on the subnet.
- */
- if (htonl(V4_PART_OF_V6(v4addr->ip_addr)) ==
- (htonl(*addr) & v4addr->ip_v4netmask))
- return (B_TRUE);
- } else if (V4_PART_OF_V6(v4addr->ip_addr) == *addr) {
+ uint32_t mask;
+
+ ASSERT(v4addr->ip_netmask >= 0 &&
+ v4addr->ip_netmask <= 32);
+ mask = 0xFFFFFFFFu << (32 - v4addr->ip_netmask);
+ /*
+ * Since we have a netmask we know this entry
+ * signifies the entire subnet. Check if the
+ * given address is on the subnet.
+ */
+ if (htonl(V4_PART_OF_V6(v4addr->ip_addr)) ==
+ (htonl(*addr) & mask))
return (B_TRUE);
- }
}
}
return (protect->mp_ipaddrcnt == 0 ?
@@ -1571,7 +1572,8 @@ ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect,
mac_ipaddr_t *v6addr = &protect->mp_ipaddrs[i];
if (v6addr->ip_version == IPV6_VERSION &&
- IN6_ARE_ADDR_EQUAL(&v6addr->ip_addr, addr))
+ IN6_ARE_PREFIXEDADDR_EQUAL(&v6addr->ip_addr, addr,
+ v6addr->ip_netmask))
return (B_TRUE);
}
return (protect->mp_ipaddrcnt == 0 ?
@@ -2107,14 +2109,23 @@ validate_ips(mac_protect_t *p)
/*
* The unspecified address is implicitly allowed
- * so there's no need to add it to the list.
+ * so there's no need to add it to the list. Also, validate that
+ * the netmask, if any, is sane for the specific version of IP.
*/
if (addr->ip_version == IPV4_VERSION) {
if (V4_PART_OF_V6(addr->ip_addr) == INADDR_ANY)
return (EINVAL);
+ if (addr->ip_netmask > 32)
+ return (EINVAL);
} else if (addr->ip_version == IPV6_VERSION) {
if (IN6_IS_ADDR_UNSPECIFIED(&addr->ip_addr))
return (EINVAL);
+
+ if (IN6_IS_ADDR_V4MAPPED_ANY(&addr->ip_addr))
+ return (EINVAL);
+
+ if (addr->ip_netmask > 128)
+ return (EINVAL);
} else {
/* invalid ip version */
return (EINVAL);
diff --git a/usr/src/uts/common/sys/mac_flow.h b/usr/src/uts/common/sys/mac_flow.h
index 4d7905de18..d43186f2b0 100644
--- a/usr/src/uts/common/sys/mac_flow.h
+++ b/usr/src/uts/common/sys/mac_flow.h
@@ -158,7 +158,7 @@ typedef enum {
typedef struct mac_ipaddr_s {
uint32_t ip_version;
in6_addr_t ip_addr;
- uint32_t ip_v4netmask;
+ uint8_t ip_netmask;
} mac_ipaddr_t;
typedef enum {