diff options
author | Cody Peter Mello <cody.mello@joyent.com> | 2016-02-26 08:52:54 -0800 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2016-03-23 21:57:43 -0700 |
commit | 797f979d1fe26bfb1cdeb3e7a86ed24c0b654200 (patch) | |
tree | 0438fca8beae8ebed526339f97afec565c777030 | |
parent | 23db4d6f668fd45f22b6b1fb668988cdf671a67b (diff) | |
download | illumos-joyent-797f979d1fe26bfb1cdeb3e7a86ed24c0b654200.tar.gz |
6684 Allow sending from IPv6 SLAAC addresses within a zone using IP spoofing protection
Reviewed by: Robert Mustacchi <rm@joyent.com>
Approved by: Dan McDonald <danmcd@omniti.com>
-rw-r--r-- | usr/src/pkg/manifests/system-test-ostest.mf | 1 | ||||
-rw-r--r-- | usr/src/test/os-tests/tests/Makefile | 2 | ||||
-rw-r--r-- | usr/src/test/os-tests/tests/spoof-ras/Makefile | 57 | ||||
-rw-r--r-- | usr/src/test/os-tests/tests/spoof-ras/spoof-ras.c | 683 | ||||
-rw-r--r-- | usr/src/uts/common/io/mac/mac_client.c | 6 | ||||
-rw-r--r-- | usr/src/uts/common/io/mac/mac_datapath_setup.c | 4 | ||||
-rw-r--r-- | usr/src/uts/common/io/mac/mac_protect.c | 339 | ||||
-rw-r--r-- | usr/src/uts/common/io/mac/mac_sched.c | 4 | ||||
-rw-r--r-- | usr/src/uts/common/sys/mac_client_impl.h | 4 | ||||
-rw-r--r-- | usr/src/uts/common/sys/mac_impl.h | 6 |
10 files changed, 1061 insertions, 45 deletions
diff --git a/usr/src/pkg/manifests/system-test-ostest.mf b/usr/src/pkg/manifests/system-test-ostest.mf index 0858f94612..fde9d5bb0a 100644 --- a/usr/src/pkg/manifests/system-test-ostest.mf +++ b/usr/src/pkg/manifests/system-test-ostest.mf @@ -32,6 +32,7 @@ file path=opt/os-tests/runfiles/omnios.run mode=0444 file path=opt/os-tests/runfiles/openindiana.run mode=0444 file path=opt/os-tests/tests/poll_test mode=0555 file path=opt/os-tests/tests/sigqueue/sigqueue_queue_size mode=0555 +file path=opt/os-tests/tests/spoof-ras mode=0555 license cr_Sun license=cr_Sun license lic_CDDL license=lic_CDDL depend fmri=system/test/testrunner type=require diff --git a/usr/src/test/os-tests/tests/Makefile b/usr/src/test/os-tests/tests/Makefile index f3958afd29..cd4104500c 100644 --- a/usr/src/test/os-tests/tests/Makefile +++ b/usr/src/test/os-tests/tests/Makefile @@ -13,6 +13,6 @@ # Copyright (c) 2012 by Delphix. All rights reserved. # -SUBDIRS = poll sigqueue +SUBDIRS = poll sigqueue spoof-ras include $(SRC)/test/Makefile.com diff --git a/usr/src/test/os-tests/tests/spoof-ras/Makefile b/usr/src/test/os-tests/tests/spoof-ras/Makefile new file mode 100644 index 0000000000..c629a03594 --- /dev/null +++ b/usr/src/test/os-tests/tests/spoof-ras/Makefile @@ -0,0 +1,57 @@ +# +# 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) 2015 by Joyent, Inc. All rights reserved. +# + +include $(SRC)/cmd/Makefile.cmd +include $(SRC)/test/Makefile.com + +PROG = spoof-ras +OBJS = $(PROG:%=%.o) +SRCS = $(OBJS:%.o=%.c) + +LDLIBS += -lsocket -lnsl +C99MODE = -xc99=%all + +ROOTOPTPKG = $(ROOT)/opt/os-tests +TESTDIR = $(ROOTOPTPKG)/tests + +CMDS = $(PROG:%=$(TESTDIR)/%) +$(CMDS) := FILEMODE = 0555 + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +%.o: ../%.c + $(COMPILE.c) $< + +install: all $(CMDS) + +lint: lint_SRCS + +clobber: clean + -$(RM) $(PROG) + +clean: + -$(RM) $(OBJS) + +$(CMDS): $(TESTDIR) $(PROG) + +$(TESTDIR): + $(INS.dir) + +$(TESTDIR)/%: % + $(INS.file) diff --git a/usr/src/test/os-tests/tests/spoof-ras/spoof-ras.c b/usr/src/test/os-tests/tests/spoof-ras/spoof-ras.c new file mode 100644 index 0000000000..e36f084f64 --- /dev/null +++ b/usr/src/test/os-tests/tests/spoof-ras/spoof-ras.c @@ -0,0 +1,683 @@ +/* + * 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 2015 Joyent, Inc. All rights reserved. + */ + +#include <strings.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <err.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/wait.h> +#include <unistd.h> +#include <signal.h> +#include <netinet/in_systm.h> /* legacy network types needed by ip_icmp.h */ +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp6.h> +#include <net/if.h> +#include <arpa/inet.h> +#include <priv.h> + +/* + * This program is meant to test the behaviour of processing incoming Router + * Advertisements when IP spoofing protection (ip-nospoof) is enabled. When + * run, it creates an etherstub on which it places two VNICs: a source VNIC, + * and a destination VNIC with protection enabled. It then sends out spoofed + * Router Advertisements with varying incorrect values. + * + * IMPORTANT: These tests expect that there is no other IPv6 traffic on the + * machine that would be delivered to a VNIC with spoofing protection enabled, + * since this would trip the DTrace probes installed by this suite of tests. + * Care should therefore be taken to not run it as a part of any series of + * tests which may be executed in such an environment, as it could lead to + * spurious failures. + */ + +#define DLADM(args...) spoof_run_proc("/usr/sbin/dladm", \ + (char *[]) { "dladm", args, NULL }) +#define IFCONFIG(args...) spoof_run_proc("/usr/sbin/ifconfig", \ + (char *[]) { "ifconfig", args, NULL }) + +typedef struct sockaddr_in6 sin6_t; +typedef int (spoof_test_f)(int, struct lif_nd_req *, sin6_t *); + +/* + * Get the link-layer address of the given interface by querying + * the neighbour cache. + */ +static int +spoof_get_lla(int s, const char *iface, struct lifreq *addrp, + struct lifreq *llap) +{ + if (strstr(iface, ":")) { + warnx("Specified interface should be the zeroth " + "logical interface on the physical device."); + } + + bzero(addrp, sizeof (*addrp)); + bzero(llap, sizeof (*llap)); + + (void) strlcpy(addrp->lifr_name, iface, LIFNAMSIZ); + if (ioctl(s, SIOCGLIFADDR, addrp) < 0) { + warn("Unable to get link-local address"); + return (-1); + } + + (void) strlcpy(llap->lifr_name, iface, LIFNAMSIZ); + bcopy(&addrp->lifr_addr, &llap->lifr_nd.lnr_addr, + sizeof (struct sockaddr_storage)); + + if (ioctl(s, SIOCLIFGETND, llap) < 0) { + warn("Failed to get link-layer address"); + return (-1); + } + + return (0); +} + +static void +spoof_prepare_lla(struct nd_opt_lla *llap, struct lif_nd_req *nce, + struct iovec *iov) +{ + uint_t optlen; + + bzero(llap, sizeof (*llap)); + llap->nd_opt_lla_type = ND_OPT_SOURCE_LINKADDR; + optlen = ((sizeof (struct nd_opt_hdr) + + nce->lnr_hdw_len + 7) / 8) * 8; + llap->nd_opt_lla_len = optlen / 8; + bcopy(&nce->lnr_hdw_addr, + &llap->nd_opt_lla_hdw_addr, nce->lnr_hdw_len); + + iov->iov_base = (caddr_t)llap; + iov->iov_len = optlen; +} + +static void +spoof_prepare_pi(const char *prefix, int prefix_len, + struct nd_opt_prefix_info *pip, struct iovec *iov) +{ + bzero(pip, sizeof (*pip)); + + pip->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; + pip->nd_opt_pi_len = 4; + pip->nd_opt_pi_prefix_len = prefix_len; + pip->nd_opt_pi_flags_reserved = + ND_OPT_PI_FLAG_AUTO | ND_OPT_PI_FLAG_ONLINK; + pip->nd_opt_pi_valid_time = 86400; + pip->nd_opt_pi_preferred_time = 86400; + if (inet_pton(AF_INET6, prefix, &pip->nd_opt_pi_prefix) == 0) { + errx(EXIT_FAILURE, "The prefix \"%s\" is " + "not a valid input prefix", prefix); + } + + iov->iov_base = (caddr_t)pip; + iov->iov_len = sizeof (*pip); +} + +static void +spoof_prepare_header(struct nd_router_advert *ichdrp, struct iovec *iov) +{ + bzero(ichdrp, sizeof (*ichdrp)); + + ichdrp->nd_ra_type = ND_ROUTER_ADVERT; + ichdrp->nd_ra_curhoplimit = 0; + + iov->iov_base = (caddr_t)ichdrp; + iov->iov_len = sizeof (*ichdrp); +} + +static int +spoof_set_max_hops(int s) +{ + int ttl = 255; + + if (setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + (char *)&ttl, sizeof (ttl)) < 0) { + warn("Failed to set IPV6_UNICAST_HOPS socket option"); + return (-1); + } + if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + (char *)&ttl, sizeof (ttl)) < 0) { + warn("Failed to set IPV6_UNICAST_HOPS socket option"); + return (-1); + } + + return (0); +} + +/* + * Send bad option lengths in the Link-Layer Source Address option + */ +static int +spoof_bad_lla_optlen_test(int s, struct lif_nd_req *nce, sin6_t *multicast) +{ + struct msghdr msg6; + struct iovec iovs[3]; + struct nd_router_advert ichdr; + struct nd_opt_lla lla; + struct nd_opt_prefix_info pi; + uint8_t old_lla_len; + + spoof_prepare_header(&ichdr, &iovs[0]); + spoof_prepare_lla(&lla, nce, &iovs[1]); + spoof_prepare_pi("fd00::", 64, &pi, &iovs[2]); + + /* Prepare message */ + bzero(&msg6, sizeof (struct msghdr)); + msg6.msg_name = multicast; + msg6.msg_namelen = sizeof (sin6_t); + msg6.msg_iov = iovs; + msg6.msg_iovlen = 3; + + old_lla_len = lla.nd_opt_lla_len; + + + /* + * Length is now smaller than the option is, so this should + * be rejected. + */ + lla.nd_opt_lla_len = 0; + if (sendmsg(s, &msg6, 0) < 0) { + warn("Failed to send ICMPv6 message"); + return (-1); + } + + /* + * Length is bigger than the option, so the following prefix + * will be offset. + */ + lla.nd_opt_lla_len = 2; + if (sendmsg(s, &msg6, 0) < 0) { + warn("Failed to send ICMPv6 message"); + return (-1); + } + + /* + * Restore the length, but shorten the amount of data to send, so we're + * sending truncated packets. (Stop before 0, so that we still send part + * of the option.) + */ + lla.nd_opt_lla_len = old_lla_len; + for (iovs[1].iov_len--; iovs[1].iov_len > 0; iovs[1].iov_len--) { + if (sendmsg(s, &msg6, 0) < 0) { + warn("Failed to send ICMPv6 message"); + return (-1); + } + } + + return (0); +} + +/* + * Send bad option lengths in the Prefix Information option + */ +static int +spoof_bad_pi_optlen_test(int s, struct lif_nd_req *nce, sin6_t *multicast) +{ + struct msghdr msg6; + struct iovec iovs[3]; + struct nd_router_advert ichdr; + struct nd_opt_lla lla; + struct nd_opt_prefix_info pi; + uint8_t old_pi_len; + + spoof_prepare_header(&ichdr, &iovs[0]); + spoof_prepare_lla(&lla, nce, &iovs[1]); + spoof_prepare_pi("fd00::", 64, &pi, &iovs[2]); + + /* Prepare message */ + bzero(&msg6, sizeof (struct msghdr)); + msg6.msg_name = multicast; + msg6.msg_namelen = sizeof (sin6_t); + msg6.msg_iov = iovs; + msg6.msg_iovlen = 3; + + old_pi_len = pi.nd_opt_pi_len; + + /* + * Length is now smaller than the option is, so this should + * be rejected. + */ + pi.nd_opt_pi_len = 0; + if (sendmsg(s, &msg6, 0) < 0) { + warn("Failed to send ICMPv6 message"); + return (-1); + } + + /* + * Length is smaller than a PI option should be. + */ + pi.nd_opt_pi_len = 3; + if (sendmsg(s, &msg6, 0) < 0) { + warn("Failed to send ICMPv6 message"); + return (-1); + } + + /* + * Length is bigger than the option, so the following prefix + * will be offset. + */ + pi.nd_opt_pi_len = 5; + if (sendmsg(s, &msg6, 0) < 0) { + warn("Failed to send ICMPv6 message"); + return (-1); + } + + /* + * Restore the length, but shorten the amount of data to send, so we're + * sending truncated packets. (Stop before 0, so that we still send part + * of the option.) + */ + pi.nd_opt_pi_len = old_pi_len; + for (iovs[2].iov_len--; iovs[2].iov_len > 0; iovs[2].iov_len--) { + if (sendmsg(s, &msg6, 0) < 0) { + warn("Failed to send ICMPv6 message"); + return (-1); + } + } + + return (0); +} + +/* + * Advertise a prefix with a prefix length greater than 128. + */ +static int +spoof_bad_plen_test(int s, struct lif_nd_req *nce, sin6_t *multicast) +{ + struct msghdr msg6; + struct iovec iovs[3]; + struct nd_router_advert ichdr; + struct nd_opt_lla lla; + struct nd_opt_prefix_info pi; + + spoof_prepare_header(&ichdr, &iovs[0]); + spoof_prepare_lla(&lla, nce, &iovs[1]); + spoof_prepare_pi("fd00::", 130, &pi, &iovs[2]); + + /* Prepare message */ + bzero(&msg6, sizeof (struct msghdr)); + msg6.msg_name = multicast; + msg6.msg_namelen = sizeof (sin6_t); + msg6.msg_iov = iovs; + msg6.msg_iovlen = 3; + + if (sendmsg(s, &msg6, 0) < 0) { + warn("Failed to send ICMPv6 message"); + return (-1); + } + + return (0); +} + +/* + * Advertise a link-local prefix, which should be disallowed and ignored. + */ +static int +spoof_link_local_test(int s, struct lif_nd_req *nce, sin6_t *multicast) +{ + struct msghdr msg6; + struct iovec iovs[3]; + struct nd_router_advert ichdr; + struct nd_opt_lla lla; + struct nd_opt_prefix_info pi; + + spoof_prepare_header(&ichdr, &iovs[0]); + spoof_prepare_lla(&lla, nce, &iovs[1]); + spoof_prepare_pi("fe80::", 64, &pi, &iovs[2]); + + /* Prepare message */ + bzero(&msg6, sizeof (struct msghdr)); + msg6.msg_name = multicast; + msg6.msg_namelen = sizeof (sin6_t); + msg6.msg_iov = iovs; + msg6.msg_iovlen = 3; + + if (sendmsg(s, &msg6, 0) < 0) { + warn("Failed to send ICMPv6 message"); + return (-1); + } + + return (0); +} + +static int +spoof_good_test(int s, struct lif_nd_req *nce, sin6_t *multicast) +{ + struct msghdr msg6; + struct iovec iovs[3]; + struct nd_router_advert ichdr; + struct nd_opt_lla lla; + struct nd_opt_prefix_info pi; + + spoof_prepare_header(&ichdr, &iovs[0]); + spoof_prepare_lla(&lla, nce, &iovs[1]); + spoof_prepare_pi("fd00::", 64, &pi, &iovs[2]); + + /* Prepare message */ + bzero(&msg6, sizeof (struct msghdr)); + msg6.msg_name = multicast; + msg6.msg_namelen = sizeof (sin6_t); + msg6.msg_iov = iovs; + msg6.msg_iovlen = 3; + + if (sendmsg(s, &msg6, 0) < 0) { + warn("Failed to send ICMPv6 message"); + return (-1); + } + + return (0); +} + +static spoof_test_f *test_cases[] = { + spoof_bad_lla_optlen_test, + spoof_bad_pi_optlen_test, + spoof_bad_plen_test, + spoof_link_local_test +}; + +static int test_cases_count = sizeof (test_cases) / sizeof (spoof_test_f *); + +static pid_t +spoof_dtrace_launch(void) +{ + pid_t child_pid = fork(); + if (child_pid == (pid_t)-1) { + err(EXIT_FAILURE, "Failed to fork to execute dtrace"); + } else if (child_pid == (pid_t)0) { + (void) execl("/usr/sbin/dtrace", "dtrace", "-q", + "-n", "sdt:mac:insert_slaac_ip:generated-addr { exit(10) }", + NULL); + err(EXIT_FAILURE, "Failed to execute dtrace"); + } + + return (child_pid); +} + +static pid_t +spoof_dtrace_wait(pid_t dtrace, int *stat) +{ + int retpid; + + /* Give time for probe to fire before checking status */ + (void) sleep(5); + + while ((retpid = waitpid(dtrace, stat, WNOHANG)) == -1) { + if (errno == EINTR) + continue; + + err(EXIT_FAILURE, "Failed to wait on child"); + } + + return (retpid); +} + +/* + * Run a function that's going to exec in a child process, and don't return + * until it exits. + */ +static int +spoof_run_proc(char *path, char *args[]) +{ + pid_t child_pid; + int childstat = 0, status = 0; + + child_pid = fork(); + if (child_pid == (pid_t)-1) { + err(EXIT_FAILURE, "Unable to fork to execute %s", path); + } else if (child_pid == (pid_t)0) { + (void) execv(path, args); + err(EXIT_FAILURE, "Failed to execute %s", path); + } + + while (waitpid(child_pid, &childstat, 0) == -1) { + if (errno == EINTR) + continue; + + warn("Failed to wait on child"); + return (-1); + } + + status = WEXITSTATUS(childstat); + if (status != 0) { + warnx("Child process %s exited with %d", path, status); + return (-1); + } + + return (0); +} + +static void +spoof_network_teardown(char *testether, char *testvnic0, char *testvnic1) +{ + // Delete dest vnic + (void) IFCONFIG(testvnic1, "inet6", "unplumb"); + (void) DLADM("delete-vnic", testvnic1); + + // Delete source vnic + (void) IFCONFIG(testvnic0, "inet6", "unplumb"); + (void) DLADM("delete-vnic", testvnic0); + + // Delete etherstub + (void) DLADM("delete-etherstub", testether); +} + +static int +spoof_network_setup(char *testether, char *testvnic0, char *testvnic1) +{ + // Create etherstub + if (DLADM("create-etherstub", "-t", testether) != 0) { + warnx("Failed to create etherstub for test network"); + return (-1); + } + + // Create source vnic + if (DLADM("create-vnic", "-t", "-l", testether, testvnic0) != 0) { + warnx("Failed to create source VNIC for test network"); + return (-1); + } + + if (IFCONFIG(testvnic0, "inet6", "plumb", "up") != 0) { + warnx("Failed to plumb source VNIC for test network"); + return (-1); + } + + // Create dest vnic + if (DLADM("create-vnic", "-t", "-l", testether, + "-p", "protection=mac-nospoof,restricted,ip-nospoof,dhcp-nospoof", + testvnic1) != 0) { + warnx("Failed to create destination VNIC for test network"); + return (-1); + } + + if (IFCONFIG(testvnic1, "inet6", "plumb", "up") != 0) { + warnx("Failed to plumb destination VNIC for test network"); + return (-1); + } + + return (0); +} + +static void +spoof_run_test(spoof_test_f *func, int s, struct lif_nd_req *nce, + sin6_t *multicast) +{ + static int cas = 1; + (void) printf("Executing test case #%d...", cas++); + if (func(s, nce, multicast) == 0) { + (void) printf(" Done.\n"); + } else { + (void) printf(" Error while running!\n"); + } +} + +static int +spoof_run_tests(int s, struct lif_nd_req *nce) +{ + int cas, stat; + pid_t dtrace; + sin6_t multicast; + + /* Prepare all-nodes multicast address */ + bzero(&multicast, sizeof (multicast)); + multicast.sin6_family = AF_INET6; + (void) inet_pton(AF_INET6, "ff02::1", &multicast.sin6_addr); + + dtrace = spoof_dtrace_launch(); + + /* Wait an adequate amount of time for the probes to be installed */ + (void) sleep(5); + + /* + * We send a packet where everything is good, except for the hop limit. + * This packet should be rejected. + */ + spoof_run_test(spoof_good_test, s, nce, &multicast); + + if (spoof_set_max_hops(s) != 0) { + warnx("Failed to set hop limit on socket"); + return (EXIT_FAILURE); + } + + for (cas = 0; cas < test_cases_count; cas++) { + spoof_run_test(test_cases[cas], s, nce, &multicast); + } + + + if (spoof_dtrace_wait(dtrace, &stat) != 0) { + (void) printf("One or more tests of bad behaviour failed!\n"); + return (EXIT_FAILURE); + } + + /* + * Now that we've executed all of the test cases that should fail, we + * can execute the test that should succeed, to make sure the normal + * case works properly. This should trip the dtrace probe. + */ + spoof_run_test(spoof_good_test, s, nce, &multicast); + + if (spoof_dtrace_wait(dtrace, &stat) != 0 && WIFEXITED(stat) && + WEXITSTATUS(stat) == 10) { + (void) printf("Tests completed successfully!\n"); + } else { + if (kill(dtrace, SIGKILL) != 0) { + warn("Failed to kill dtrace child (pid %d)", dtrace); + } + (void) printf("Test of normal behaviour didn't succeed!\n"); + return (EXIT_FAILURE); + } + + return (0); +} + +/* + * Make sure that we have all of the privileges we need to execute these tests, + * so that we can error out before we would fail. + */ +void +spoof_check_privs(void) +{ + priv_set_t *privset = priv_allocset(); + + if (privset == NULL) { + err(EXIT_FAILURE, "Failed to allocate memory for " + "checking privileges"); + } + + if (getppriv(PRIV_EFFECTIVE, privset) != 0) { + err(EXIT_FAILURE, "Failed to get current privileges"); + } + + if (!priv_ismember(privset, PRIV_DTRACE_KERNEL)) { + errx(EXIT_FAILURE, "These tests need to be run as a user " + "capable of tracing the kernel."); + } + + if (!priv_ismember(privset, PRIV_SYS_NET_CONFIG)) { + errx(EXIT_FAILURE, "These tests need to be run as a user " + "capable of creating and configuring network interfaces."); + } + + if (!priv_ismember(privset, PRIV_NET_ICMPACCESS)) { + errx(EXIT_FAILURE, "These tests need to be run as a user " + "capable of sending ICMP packets."); + } + + priv_freeset(privset); +} + +int +main(void) +{ + struct lifreq addr, llar; + int error, s; + char testether[LIFNAMSIZ]; + char testvnic0[LIFNAMSIZ]; + char testvnic1[LIFNAMSIZ]; + pid_t curpid = getpid(); + + spoof_check_privs(); + + /* + * Set up the socket and test network for sending + */ + s = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + if (s < 0) { + err(EXIT_FAILURE, "Failed to open ICMPv6 socket"); + } + + (void) snprintf(testether, sizeof (testether), "testether%d", curpid); + (void) snprintf(testvnic0, sizeof (testvnic0), "testvnic%d_0", curpid); + (void) snprintf(testvnic1, sizeof (testvnic1), "testvnic%d_1", curpid); + + if (spoof_network_setup(testether, testvnic0, testvnic1) != 0) { + warnx("Failed to set up test network"); + error = EXIT_FAILURE; + goto cleanup; + } + + if (spoof_get_lla(s, testvnic0, &addr, &llar) != 0) { + warnx("Failed to get link-layer address"); + error = EXIT_FAILURE; + goto cleanup; + } + + if (setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_IF, + (char *)&((sin6_t *)&addr.lifr_addr)->sin6_scope_id, + sizeof (int)) < 0) { + warn("Failed to set IPV6_UNICAST_HOPS socket option"); + return (-1); + } + + if (bind(s, (struct sockaddr *)&addr.lifr_addr, sizeof (sin6_t)) != 0) { + warnx("Failed to bind to link-local address"); + error = EXIT_FAILURE; + goto cleanup; + } + + error = spoof_run_tests(s, &llar.lifr_nd); + +cleanup: + if (close(s) != 0) { + warnx("Failed to close ICMPv6 socket"); + } + spoof_network_teardown(testether, testvnic0, testvnic1); + return (error); +} diff --git a/usr/src/uts/common/io/mac/mac_client.c b/usr/src/uts/common/io/mac/mac_client.c index 85e92930c1..8f0ec9eb67 100644 --- a/usr/src/uts/common/io/mac/mac_client.c +++ b/usr/src/uts/common/io/mac/mac_client.c @@ -858,10 +858,10 @@ mac_unicast_update_client_flow(mac_client_impl_t *mcip) mac_flow_set_desc(flent, &flow_desc); /* - * The v6 local addr (used by mac protection) needs to be + * The v6 local and SLAAC addrs (used by mac protection) need to be * regenerated because our mac address has changed. */ - mac_protect_update_v6_local_addr(mcip); + mac_protect_update_mac_token(mcip); /* * A MAC client could have one MAC address but multiple @@ -2956,7 +2956,7 @@ mac_client_datapath_teardown(mac_client_handle_t mch, mac_unicast_impl_t *muip, if (muip != NULL) kmem_free(muip, sizeof (mac_unicast_impl_t)); mac_protect_cancel_timer(mcip); - mac_protect_flush_dhcp(mcip); + mac_protect_flush_dynamic(mcip); bzero(&mcip->mci_misc_stat, sizeof (mcip->mci_misc_stat)); /* diff --git a/usr/src/uts/common/io/mac/mac_datapath_setup.c b/usr/src/uts/common/io/mac/mac_datapath_setup.c index 5ca673ea6e..14d94981cd 100644 --- a/usr/src/uts/common/io/mac/mac_datapath_setup.c +++ b/usr/src/uts/common/io/mac/mac_datapath_setup.c @@ -3018,8 +3018,8 @@ mac_datapath_setup(mac_client_impl_t *mcip, flow_entry_t *flent, goto setup_failed; mcip->mci_unicast = mac_find_macaddr(mip, mac_addr); ASSERT(mcip->mci_unicast != NULL); - /* Initialize the v6 local addr used by link protection */ - mac_protect_update_v6_local_addr(mcip); + /* (Re)init the v6 token & local addr used by link protection */ + mac_protect_update_mac_token(mcip); break; default: diff --git a/usr/src/uts/common/io/mac/mac_protect.c b/usr/src/uts/common/io/mac/mac_protect.c index 4438cb90fb..805b5d36f9 100644 --- a/usr/src/uts/common/io/mac/mac_protect.c +++ b/usr/src/uts/common/io/mac/mac_protect.c @@ -27,6 +27,7 @@ * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ +#include <sys/cmn_err.h> #include <sys/strsun.h> #include <sys/sdt.h> #include <sys/mac.h> @@ -145,6 +146,7 @@ */ static ulong_t dhcp_max_pending_txn = 512; static ulong_t dhcp_max_completed_txn = 512; +static ulong_t slaac_max_allowed = 512; static hrtime_t txn_cleanup_interval = 60 * NANOSEC; /* @@ -196,6 +198,16 @@ typedef struct dhcpv6_txn { struct dhcpv6_txn *dt_next; } dhcpv6_txn_t; +/* + * Stateless address autoconfiguration (SLAAC) address. May be added to + * mci_v6_slaac_ip. + */ +typedef struct slaac_addr { + in6_addr_t sla_prefix; + in6_addr_t sla_addr; + avl_node_t sla_node; +} slaac_addr_t; + static void start_txn_cleanup_timer(mac_client_impl_t *); static boolean_t allowed_ips_set(mac_resource_props_t *, uint32_t); @@ -654,16 +666,13 @@ done: * Core logic for intercepting inbound DHCPv4 packets. */ static void -intercept_dhcpv4_inbound(mac_client_impl_t *mcip, ipha_t *ipha, uchar_t *end) +intercept_dhcpv4_inbound(mac_client_impl_t *mcip, uchar_t *end, + struct dhcp *dh4) { uchar_t *opt; - struct dhcp *dh4; dhcpv4_txn_t *txn, *ctxn; uint8_t opt_len, mtype; - if (get_dhcpv4_info(ipha, end, &dh4) != 0) - return; - if (get_dhcpv4_option(dh4, end, CD_DHCP_TYPE, &opt, &opt_len) != 0 || opt_len != 1) { DTRACE_PROBE2(mtype__not__found, mac_client_impl_t *, mcip, @@ -771,6 +780,21 @@ compare_dhcpv6_cid(const void *arg1, const void *arg2) return (0); } +static int +compare_slaac_ip(const void *arg1, const void *arg2) +{ + const slaac_addr_t *ip1 = arg1, *ip2 = arg2; + int ret; + + ret = memcmp(&ip1->sla_addr, &ip2->sla_addr, sizeof (in6_addr_t)); + if (ret < 0) + return (-1); + else if (ret > 0) + return (1); + else + return (0); +} + /* * Locate the start of a DHCPv6 header. * The possible return values and associated meanings are: @@ -829,6 +853,51 @@ get_dhcpv6_info(ip6_t *ip6h, uchar_t *end, dhcpv6_message_t **dh6) return (0); } +static int +get_ra_info(ip6_t *ip6h, uchar_t *end, nd_router_advert_t **ra) +{ + uint16_t hdrlen; + ip6_frag_t *frag = NULL; + uint8_t proto; + uchar_t *hdrp; + struct icmp6_hdr *icmp; + + if (!mac_ip_hdr_length_v6(ip6h, end, &hdrlen, &proto, &frag)) + return (ENOSPC); + + if (proto != IPPROTO_ICMPV6) + return (EINVAL); + + if (frag != NULL) { + /* + * All non-initial fragments may pass because we cannot + * identify their type. It's safe to let them through + * because reassembly will fail if we decide to drop the + * initial fragment. + */ + if ((ntohs(frag->ip6f_offlg) & ~7) != 0) + return (EINVAL); + return (ENOSPC); + } + + /* + * Ensure that the ICMP header falls w/in packet boundaries, in case + * we've received a malicious packet that reports incorrect lengths. + */ + hdrp = (uchar_t *)ip6h + hdrlen; + if ((hdrp + sizeof (struct icmp6_hdr)) > end) { + return (EINVAL); + } + icmp = (struct icmp6_hdr *)hdrp; + + if (icmp->icmp6_type != ND_ROUTER_ADVERT || + icmp->icmp6_code != 0) + return (EINVAL); + + *ra = (nd_router_advert_t *)icmp; + return (0); +} + /* * Find the specified DHCPv6 option. */ @@ -1172,6 +1241,18 @@ flush_dhcpv6(mac_client_impl_t *mcip) } } +void +flush_slaac(mac_client_impl_t *mcip) +{ + void *cookie = NULL; + slaac_addr_t *addr = NULL; + + while ((addr = avl_destroy_nodes(&mcip->mci_v6_slaac_ip, &cookie)) != + NULL) { + kmem_free(addr, sizeof (slaac_addr_t)); + } +} + /* * Cleanup stale DHCPv6 transactions. */ @@ -1287,17 +1368,14 @@ done: * Core logic for intercepting inbound DHCPv6 packets. */ static void -intercept_dhcpv6_inbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end) +intercept_dhcpv6_inbound(mac_client_impl_t *mcip, uchar_t *end, + dhcpv6_message_t *dh6) { - dhcpv6_message_t *dh6; dhcpv6_txn_t *txn; uint32_t xid; uint8_t mtype; uint16_t status; - if (get_dhcpv6_info(ip6h, end, &dh6) != 0) - return; - mtype = dh6->d6m_msg_type; if (mtype != DHCPV6_MSG_REPLY) return; @@ -1340,6 +1418,134 @@ done: } /* + * Check whether an IP address is in the SLAAC table. + */ +static boolean_t +check_slaac_ip(mac_client_impl_t *mcip, in6_addr_t *addr) +{ + slaac_addr_t tmp_addr, *a; + + mutex_enter(&mcip->mci_protect_lock); + bcopy(addr, &tmp_addr.sla_addr, sizeof (in6_addr_t)); + a = avl_find(&mcip->mci_v6_slaac_ip, &tmp_addr, NULL); + mutex_exit(&mcip->mci_protect_lock); + return (a != NULL); +} + +static boolean_t +insert_slaac_ip(avl_tree_t *tree, in6_addr_t *token, slaac_addr_t *addr) +{ + uint_t i; + avl_index_t where; + in6_addr_t *prefix = &addr->sla_prefix; + in6_addr_t *in6p = &addr->sla_addr; + + bcopy(prefix, in6p, sizeof (struct in6_addr)); + + for (i = 0; i < 4; i++) { + in6p->s6_addr32[i] = token->s6_addr32[i] | + in6p->s6_addr32[i]; + } + + DTRACE_PROBE1(generated__addr, in6_addr_t *, in6p); + + if (avl_find(tree, addr, &where) != NULL) + return (B_FALSE); + + avl_insert(tree, addr, where); + return (B_TRUE); +} + +static void +insert_slaac_prefix(mac_client_impl_t *mcip, nd_opt_prefix_info_t *po) +{ + slaac_addr_t *addr = NULL; + in6_addr_t *token = &mcip->mci_v6_mac_token; + + ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); + + if (avl_numnodes(&mcip->mci_v6_slaac_ip) >= slaac_max_allowed) { + DTRACE_PROBE(limit__reached); + return; + } + + if ((addr = kmem_zalloc(sizeof (slaac_addr_t), + KM_NOSLEEP | KM_NORMALPRI)) == NULL) + return; + + bcopy(&po->nd_opt_pi_prefix, &addr->sla_prefix, + sizeof (struct in6_addr)); + + if (!insert_slaac_ip(&mcip->mci_v6_slaac_ip, token, addr)) { + kmem_free(addr, sizeof (slaac_addr_t)); + } +} + +static void +intercept_prefix_info(mac_client_impl_t *mcip, nd_opt_prefix_info_t *po) +{ + if (8 * po->nd_opt_pi_len != sizeof (nd_opt_prefix_info_t)) { + DTRACE_PROBE(invalid__length); + return; + } + + if (po->nd_opt_pi_prefix_len > 128) { + DTRACE_PROBE(invalid__plen); + return; + } + + if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) { + DTRACE_PROBE(link__local); + return; + } + + if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) == 0) + return; + + mutex_enter(&mcip->mci_protect_lock); + insert_slaac_prefix(mcip, po); + mutex_exit(&mcip->mci_protect_lock); +} + +/* + * If we receive a Router Advertisement carrying prefix information and + * indicating that SLAAC should be performed, then track the prefix. + */ +static void +intercept_ra_inbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end, + nd_router_advert_t *ra) +{ + struct nd_opt_hdr *opt; + int len, optlen; + + if (ip6h->ip6_hlim != 255) { + DTRACE_PROBE1(invalid__hoplimit, uint8_t, ip6h->ip6_hlim); + return; + } + + len = ip6h->ip6_plen - sizeof (nd_router_advert_t); + opt = (struct nd_opt_hdr *)&ra[1]; + while (len >= sizeof (struct nd_opt_hdr) && + ((uchar_t *)opt + sizeof (struct nd_opt_hdr)) <= end) { + optlen = opt->nd_opt_len * 8; + + if (optlen < sizeof (struct nd_opt_hdr) || + ((uchar_t *)opt + optlen) > end) { + DTRACE_PROBE(invalid__length); + return; + } + + if (opt->nd_opt_type == ND_OPT_PREFIX_INFORMATION) { + intercept_prefix_info(mcip, + (nd_opt_prefix_info_t *)opt); + } + + opt = (struct nd_opt_hdr *)((char *)opt + optlen); + len -= optlen; + } +} + +/* * Timer for cleaning up stale transactions. */ static void @@ -1446,7 +1652,7 @@ get_l3_info(mblk_t *mp, size_t hdrsize, uchar_t **start, uchar_t **end, } void -mac_protect_intercept_dhcp_one(mac_client_impl_t *mcip, mblk_t *mp) +mac_protect_intercept_dynamic_one(mac_client_impl_t *mcip, mblk_t *mp) { mac_impl_t *mip = mcip->mci_mip; uchar_t *start, *end; @@ -1470,21 +1676,31 @@ mac_protect_intercept_dhcp_one(mac_client_impl_t *mcip, mblk_t *mp) switch (mhi.mhi_bindsap) { case ETHERTYPE_IP: { - ipha_t *ipha = (ipha_t *)start; + struct dhcp *dh4; + ipha_t *ipha = (ipha_t *)start; if (start + sizeof (ipha_t) > end) return; - intercept_dhcpv4_inbound(mcip, ipha, end); + if (get_dhcpv4_info(ipha, end, &dh4) == 0) { + intercept_dhcpv4_inbound(mcip, end, dh4); + } break; } case ETHERTYPE_IPV6: { - ip6_t *ip6h = (ip6_t *)start; + dhcpv6_message_t *dh6; + nd_router_advert_t *ra; + ip6_t *ip6h = (ip6_t *)start; if (start + sizeof (ip6_t) > end) return; - intercept_dhcpv6_inbound(mcip, ip6h, end); + if (get_dhcpv6_info(ip6h, end, &dh6) == 0) { + intercept_dhcpv6_inbound(mcip, end, dh6); + } else if (get_ra_info(ip6h, end, &ra) == 0) { + intercept_ra_inbound(mcip, ip6h, end, ra); + } + break; } } @@ -1492,7 +1708,7 @@ mac_protect_intercept_dhcp_one(mac_client_impl_t *mcip, mblk_t *mp) } void -mac_protect_intercept_dhcp(mac_client_impl_t *mcip, mblk_t *mp) +mac_protect_intercept_dynamic(mac_client_impl_t *mcip, mblk_t *mp) { /* * Skip checks if we are part of an aggr. @@ -1501,15 +1717,16 @@ mac_protect_intercept_dhcp(mac_client_impl_t *mcip, mblk_t *mp) return; for (; mp != NULL; mp = mp->b_next) - mac_protect_intercept_dhcp_one(mcip, mp); + mac_protect_intercept_dynamic_one(mcip, mp); } void -mac_protect_flush_dhcp(mac_client_impl_t *mcip) +mac_protect_flush_dynamic(mac_client_impl_t *mcip) { mutex_enter(&mcip->mci_protect_lock); flush_dhcpv4(mcip); flush_dhcpv6(mcip); + flush_slaac(mcip); mutex_exit(&mcip->mci_protect_lock); } @@ -1586,8 +1803,13 @@ ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect, v6addr->ip_netmask)) return (B_TRUE); } - return (protect->mp_ipaddrcnt == 0 ? - check_dhcpv6_dyn_ip(mcip, addr) : B_FALSE); + + if (protect->mp_ipaddrcnt == 0) { + return (check_slaac_ip(mcip, addr) || + check_dhcpv6_dyn_ip(mcip, addr)); + } else { + return (B_FALSE); + } } /* @@ -1943,19 +2165,68 @@ fail: } /* - * This needs to be called whenever the mac client's mac address changes. + * This is called whenever the mac client's mac address changes, to make sure + * we allow use of the new link-local address. */ -void +static void mac_protect_update_v6_local_addr(mac_client_impl_t *mcip) { - uint8_t *p, *macaddr = mcip->mci_unicast->ma_addr; - uint_t i, media = mcip->mci_mip->mi_info.mi_media; - in6_addr_t token, *v6addr = &mcip->mci_v6_local_addr; + uint_t i; + in6_addr_t *token = &mcip->mci_v6_mac_token; + in6_addr_t *v6addr = &mcip->mci_v6_local_addr; in6_addr_t ll_template = {(uint32_t)V6_LINKLOCAL, 0x0, 0x0, 0x0}; + for (i = 0; i < 4; i++) { + v6addr->s6_addr32[i] = token->s6_addr32[i] | + ll_template.s6_addr32[i]; + } + mcip->mci_protect_flags |= MPT_FLAG_V6_LOCAL_ADDR_SET; +} - bzero(&token, sizeof (token)); - p = (uint8_t *)&token.s6_addr32[2]; +/* + * This is called whenever the mac client's mac address changes, to make sure + * that any existing addresses gained via SLAAC are appropriately updated. + */ +static void +mac_protect_update_v6_slaac_addr(mac_client_impl_t *mcip) +{ + void *cookie = NULL; + avl_tree_t temp_tree; + avl_tree_t *ttp = &temp_tree, *sip = &mcip->mci_v6_slaac_ip; + in6_addr_t *token = &mcip->mci_v6_mac_token; + slaac_addr_t *addr = NULL; + + avl_create(ttp, compare_slaac_ip, sizeof (slaac_addr_t), + offsetof(slaac_addr_t, sla_node)); + + /* Copy everything over to the temporary tree, and fix the IP address */ + while ((addr = avl_destroy_nodes(sip, &cookie)) != NULL) { + VERIFY(insert_slaac_ip(ttp, token, addr) == B_TRUE); + } + + /* + * Now that the tempory tree has all of the modified addresses, we can + * swap them over to the original tree once it's reset. + */ + avl_destroy(sip); + avl_create(sip, compare_slaac_ip, sizeof (slaac_addr_t), + offsetof(slaac_addr_t, sla_node)); + avl_swap(ttp, sip); +} + +/* + * After the unicast MAC address changes, we need to update the derived token, + * and update the IPv6 addresses that use the token. + */ +void +mac_protect_update_mac_token(mac_client_impl_t *mcip) +{ + uint_t media = mcip->mci_mip->mi_info.mi_media; + uint8_t *p, *macaddr = mcip->mci_unicast->ma_addr; + in6_addr_t *token = &mcip->mci_v6_mac_token; + + bzero(token, sizeof (in6_addr_t)); + p = (uint8_t *)&token->s6_addr32[2]; switch (media) { case DL_ETHER: @@ -1974,19 +2245,18 @@ mac_protect_update_v6_local_addr(mac_client_impl_t *mcip) /* * We do not need to generate the local address for link types * that do not support link protection. Wifi pretends to be - * ethernet so it is covered by the DL_ETHER case (note the + * Ethernet so it is covered by the DL_ETHER case (note the * use of mi_media instead of mi_nativemedia). */ return; } - for (i = 0; i < 4; i++) { - v6addr->s6_addr32[i] = token.s6_addr32[i] | - ll_template.s6_addr32[i]; - } - mcip->mci_protect_flags |= MPT_FLAG_V6_LOCAL_ADDR_SET; + mac_protect_update_v6_local_addr(mcip); + mac_protect_update_v6_slaac_addr(mcip); } + + /* * Enforce link protection on one packet. */ @@ -2304,6 +2574,8 @@ mac_protect_init(mac_client_impl_t *mcip) sizeof (dhcpv6_cid_t), offsetof(dhcpv6_cid_t, dc_node)); avl_create(&mcip->mci_v6_dyn_ip, compare_dhcpv6_ip, sizeof (dhcpv6_addr_t), offsetof(dhcpv6_addr_t, da_node)); + avl_create(&mcip->mci_v6_slaac_ip, compare_slaac_ip, + sizeof (slaac_addr_t), offsetof(slaac_addr_t, sla_node)); } void @@ -2315,6 +2587,7 @@ mac_protect_fini(mac_client_impl_t *mcip) avl_destroy(&mcip->mci_v4_dyn_ip); avl_destroy(&mcip->mci_v4_completed_txn); avl_destroy(&mcip->mci_v4_pending_txn); + avl_destroy(&mcip->mci_v6_slaac_ip); mcip->mci_txn_cleanup_tid = 0; mcip->mci_protect_flags = 0; mutex_destroy(&mcip->mci_protect_lock); diff --git a/usr/src/uts/common/io/mac/mac_sched.c b/usr/src/uts/common/io/mac/mac_sched.c index eb179b07c7..148f739d52 100644 --- a/usr/src/uts/common/io/mac/mac_sched.c +++ b/usr/src/uts/common/io/mac/mac_sched.c @@ -2632,7 +2632,7 @@ again: } if (MAC_PROTECT_ENABLED(mcip, MPT_IPNOSPOOF)) { mutex_exit(&mac_srs->srs_lock); - mac_protect_intercept_dhcp(mcip, head); + mac_protect_intercept_dynamic(mcip, head); mutex_enter(&mac_srs->srs_lock); } } @@ -2910,7 +2910,7 @@ again: } if (MAC_PROTECT_ENABLED(mcip, MPT_IPNOSPOOF)) { mutex_exit(&mac_srs->srs_lock); - mac_protect_intercept_dhcp(mcip, head); + mac_protect_intercept_dynamic(mcip, head); mutex_enter(&mac_srs->srs_lock); } } diff --git a/usr/src/uts/common/sys/mac_client_impl.h b/usr/src/uts/common/sys/mac_client_impl.h index 3c89b7c434..0904b28645 100644 --- a/usr/src/uts/common/sys/mac_client_impl.h +++ b/usr/src/uts/common/sys/mac_client_impl.h @@ -96,7 +96,7 @@ typedef union mac_tx_percpu_s { #define pcpu_tx_refcnt pcpu_lr._pcpu_tx_refcnt /* - * One of these is instanciated for each MAC client. + * One of these is instantiated for each MAC client. */ struct mac_client_impl_s { /* Protected by */ struct mac_client_impl_s *mci_client_next; /* mi_rw_lock */ @@ -182,6 +182,7 @@ struct mac_client_impl_s { /* Protected by */ */ kmutex_t mci_protect_lock; uint32_t mci_protect_flags; /* SL */ + in6_addr_t mci_v6_mac_token; /* SL */ in6_addr_t mci_v6_local_addr; /* SL */ avl_tree_t mci_v4_pending_txn; /* mci_protect_lock */ avl_tree_t mci_v4_completed_txn; /* mci_protect_lock */ @@ -189,6 +190,7 @@ struct mac_client_impl_s { /* Protected by */ avl_tree_t mci_v6_pending_txn; /* mci_protect_lock */ avl_tree_t mci_v6_cid; /* mci_protect_lock */ avl_tree_t mci_v6_dyn_ip; /* mci_protect_lock */ + avl_tree_t mci_v6_slaac_ip; /* mci_protect_lock */ timeout_id_t mci_txn_cleanup_tid; /* mci_protect_lock */ /* diff --git a/usr/src/uts/common/sys/mac_impl.h b/usr/src/uts/common/sys/mac_impl.h index 3c9c6b77a4..2286b587e8 100644 --- a/usr/src/uts/common/sys/mac_impl.h +++ b/usr/src/uts/common/sys/mac_impl.h @@ -884,9 +884,9 @@ extern int mac_protect_set(mac_client_handle_t, mac_resource_props_t *); extern boolean_t mac_protect_enabled(mac_client_handle_t, uint32_t); extern int mac_protect_validate(mac_resource_props_t *); extern void mac_protect_update(mac_resource_props_t *, mac_resource_props_t *); -extern void mac_protect_update_v6_local_addr(mac_client_impl_t *); -extern void mac_protect_intercept_dhcp(mac_client_impl_t *, mblk_t *); -extern void mac_protect_flush_dhcp(mac_client_impl_t *); +extern void mac_protect_update_mac_token(mac_client_impl_t *); +extern void mac_protect_intercept_dynamic(mac_client_impl_t *, mblk_t *); +extern void mac_protect_flush_dynamic(mac_client_impl_t *); extern void mac_protect_cancel_timer(mac_client_impl_t *); extern void mac_protect_init(mac_client_impl_t *); extern void mac_protect_fini(mac_client_impl_t *); |