Home | History | Annotate | Line # | Download | only in npf
      1   1.1     rmind /*-
      2  1.11     rmind  * Copyright (c) 2019 Mindaugas Rasiukevicius <rmind at noxt eu>
      3   1.1     rmind  * Copyright (c) 2013 The NetBSD Foundation, Inc.
      4   1.1     rmind  * All rights reserved.
      5   1.1     rmind  *
      6   1.1     rmind  * This code is derived from software contributed to The NetBSD Foundation
      7   1.1     rmind  * by Mindaugas Rasiukevicius.
      8   1.1     rmind  *
      9   1.1     rmind  * Redistribution and use in source and binary forms, with or without
     10   1.1     rmind  * modification, are permitted provided that the following conditions
     11   1.1     rmind  * are met:
     12   1.1     rmind  * 1. Redistributions of source code must retain the above copyright
     13   1.1     rmind  *    notice, this list of conditions and the following disclaimer.
     14   1.1     rmind  * 2. Redistributions in binary form must reproduce the above copyright
     15   1.1     rmind  *    notice, this list of conditions and the following disclaimer in the
     16   1.1     rmind  *    documentation and/or other materials provided with the distribution.
     17   1.1     rmind  *
     18   1.1     rmind  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     19   1.1     rmind  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     20   1.1     rmind  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     21   1.1     rmind  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     22   1.1     rmind  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     23   1.1     rmind  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     24   1.1     rmind  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     25   1.1     rmind  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     26   1.1     rmind  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     27   1.1     rmind  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     28   1.1     rmind  * POSSIBILITY OF SUCH DAMAGE.
     29   1.1     rmind  */
     30   1.1     rmind 
     31   1.1     rmind /*
     32  1.11     rmind  * NPF network interface handling.
     33   1.1     rmind  *
     34  1.11     rmind  * NPF uses its own interface IDs (npf-if-id).  These IDs start from 1.
     35  1.11     rmind  * Zero is reserved to indicate "no interface" case or an interface of
     36  1.11     rmind  * no interest (i.e. not registered).
     37   1.5     rmind  *
     38  1.11     rmind  * This module provides an interface to primarily handle the following:
     39   1.1     rmind  *
     40  1.11     rmind  * - Bind a symbolic interface name to NPF interface ID.
     41  1.11     rmind  * - Associate NPF interface ID when the network interface is attached.
     42  1.11     rmind  *
     43  1.11     rmind  * When NPF configuration is (re)loaded, each referenced network interface
     44  1.11     rmind  * name is registered with a unique ID.  If the network interface is already
     45  1.11     rmind  * attached, then the ID is associated with it immediately; otherwise, IDs
     46  1.11     rmind  * are associated/disassociated on interface events which are monitored
     47  1.11     rmind  * using pfil(9) hooks.
     48  1.11     rmind  *
     49  1.11     rmind  * To avoid race conditions when an active NPF configuration is updated or
     50  1.11     rmind  * interfaces are detached/attached, the interface names are never removed
     51  1.11     rmind  * and therefore IDs are never re-assigned.  The only point when interface
     52  1.11     rmind  * names and IDs are cleared is when the configuration is flushed.
     53  1.11     rmind  *
     54  1.11     rmind  * A linear counter is used for IDs.
     55   1.1     rmind  */
     56   1.1     rmind 
     57   1.7  christos #ifdef _KERNEL
     58   1.1     rmind #include <sys/cdefs.h>
     59  1.13     rmind __KERNEL_RCSID(0, "$NetBSD: npf_if.c,v 1.13 2020/05/30 14:16:56 rmind Exp $");
     60   1.1     rmind 
     61   1.1     rmind #include <sys/param.h>
     62   1.1     rmind #include <sys/types.h>
     63   1.1     rmind #include <sys/kmem.h>
     64   1.1     rmind #include <net/if.h>
     65   1.7  christos #endif
     66   1.1     rmind 
     67   1.1     rmind #include "npf_impl.h"
     68   1.1     rmind 
     69   1.7  christos typedef struct npf_ifmap {
     70  1.11     rmind 	char		ifname[IFNAMSIZ + 1];
     71   1.1     rmind } npf_ifmap_t;
     72   1.1     rmind 
     73  1.11     rmind #define	NPF_IFMAP_NOID			(0U)
     74  1.11     rmind #define	NPF_IFMAP_SLOT2ID(npf, slot)	((npf)->ifmap_off + (slot) + 1)
     75  1.13     rmind #define	NPF_IFMAP_ID2SLOT(npf, id)	\
     76  1.13     rmind     ((id) - atomic_load_relaxed(&(npf)->ifmap_off) - 1)
     77  1.11     rmind 
     78   1.7  christos void
     79   1.7  christos npf_ifmap_init(npf_t *npf, const npf_ifops_t *ifops)
     80   1.7  christos {
     81   1.7  christos 	const size_t nbytes = sizeof(npf_ifmap_t) * NPF_MAX_IFMAP;
     82   1.7  christos 
     83   1.7  christos 	KASSERT(ifops != NULL);
     84  1.13     rmind 	ifops->flush(npf, (void *)(uintptr_t)0);
     85   1.7  christos 
     86  1.11     rmind 	mutex_init(&npf->ifmap_lock, MUTEX_DEFAULT, IPL_SOFTNET);
     87   1.7  christos 	npf->ifmap = kmem_zalloc(nbytes, KM_SLEEP);
     88   1.7  christos 	npf->ifmap_cnt = 0;
     89  1.11     rmind 	npf->ifmap_off = 0;
     90   1.7  christos 	npf->ifops = ifops;
     91   1.7  christos }
     92   1.7  christos 
     93   1.7  christos void
     94   1.7  christos npf_ifmap_fini(npf_t *npf)
     95   1.7  christos {
     96   1.7  christos 	const size_t nbytes = sizeof(npf_ifmap_t) * NPF_MAX_IFMAP;
     97  1.11     rmind 	mutex_destroy(&npf->ifmap_lock);
     98   1.7  christos 	kmem_free(npf->ifmap, nbytes);
     99   1.7  christos }
    100   1.1     rmind 
    101  1.11     rmind static unsigned
    102   1.7  christos npf_ifmap_lookup(npf_t *npf, const char *ifname)
    103   1.1     rmind {
    104  1.11     rmind 	KASSERT(mutex_owned(&npf->ifmap_lock));
    105   1.1     rmind 
    106  1.11     rmind 	for (unsigned i = 0; i < npf->ifmap_cnt; i++) {
    107  1.11     rmind 		npf_ifmap_t *ifmap = &npf->ifmap[i];
    108   1.1     rmind 
    109  1.11     rmind 		if (strcmp(ifmap->ifname, ifname) == 0) {
    110  1.11     rmind 			return NPF_IFMAP_SLOT2ID(npf, i);
    111  1.11     rmind 		}
    112   1.1     rmind 	}
    113  1.11     rmind 	return NPF_IFMAP_NOID;
    114   1.1     rmind }
    115   1.1     rmind 
    116  1.11     rmind /*
    117  1.11     rmind  * npf_ifmap_register: register an interface name; return an assigned
    118  1.11     rmind  * NPF network ID on success (non-zero).
    119  1.11     rmind  *
    120  1.11     rmind  * This routine is mostly called on NPF configuration (re)load for the
    121  1.11     rmind  * interfaces names referenced by the rules.
    122  1.11     rmind  */
    123  1.11     rmind unsigned
    124   1.7  christos npf_ifmap_register(npf_t *npf, const char *ifname)
    125   1.1     rmind {
    126  1.11     rmind 	npf_ifmap_t *ifmap;
    127  1.11     rmind 	unsigned id, i;
    128   1.1     rmind 	ifnet_t *ifp;
    129   1.1     rmind 
    130  1.11     rmind 	mutex_enter(&npf->ifmap_lock);
    131  1.11     rmind 	if ((id = npf_ifmap_lookup(npf, ifname)) != NPF_IFMAP_NOID) {
    132   1.1     rmind 		goto out;
    133   1.1     rmind 	}
    134  1.11     rmind 	if (npf->ifmap_cnt == NPF_MAX_IFMAP) {
    135  1.11     rmind 		printf("npf_ifmap_new: out of slots; bump NPF_MAX_IFMAP\n");
    136  1.11     rmind 		id = NPF_IFMAP_NOID;
    137   1.1     rmind 		goto out;
    138   1.1     rmind 	}
    139  1.11     rmind 	KASSERT(npf->ifmap_cnt < NPF_MAX_IFMAP);
    140  1.11     rmind 
    141  1.11     rmind 	/* Allocate a new slot and convert and assign an ID. */
    142  1.11     rmind 	i = npf->ifmap_cnt++;
    143  1.11     rmind 	ifmap = &npf->ifmap[i];
    144  1.11     rmind 	strlcpy(ifmap->ifname, ifname, IFNAMSIZ);
    145  1.11     rmind 	id = NPF_IFMAP_SLOT2ID(npf, i);
    146   1.1     rmind 
    147  1.13     rmind 	if ((ifp = npf->ifops->lookup(npf, ifname)) != NULL) {
    148  1.13     rmind 		npf->ifops->setmeta(npf, ifp, (void *)(uintptr_t)id);
    149   1.1     rmind 	}
    150   1.1     rmind out:
    151  1.11     rmind 	mutex_exit(&npf->ifmap_lock);
    152  1.11     rmind 	return id;
    153   1.1     rmind }
    154   1.1     rmind 
    155   1.1     rmind void
    156   1.7  christos npf_ifmap_flush(npf_t *npf)
    157   1.1     rmind {
    158  1.11     rmind 	mutex_enter(&npf->ifmap_lock);
    159  1.13     rmind 	npf->ifops->flush(npf, (void *)(uintptr_t)NPF_IFMAP_NOID);
    160  1.11     rmind 	for (unsigned i = 0; i < npf->ifmap_cnt; i++) {
    161  1.11     rmind 		npf->ifmap[i].ifname[0] = '\0';
    162  1.11     rmind 	}
    163  1.11     rmind 	npf->ifmap_cnt = 0;
    164   1.1     rmind 
    165  1.11     rmind 	/*
    166  1.11     rmind 	 * Reset the ID counter if reaching the overflow; this is not
    167  1.11     rmind 	 * realistic, but we maintain correctness.
    168  1.11     rmind 	 */
    169  1.11     rmind 	if (npf->ifmap_off < (UINT_MAX - NPF_MAX_IFMAP)) {
    170  1.11     rmind 		npf->ifmap_off += NPF_MAX_IFMAP;
    171  1.11     rmind 	} else {
    172  1.11     rmind 		npf->ifmap_off = 0;
    173   1.1     rmind 	}
    174  1.11     rmind 	mutex_exit(&npf->ifmap_lock);
    175   1.1     rmind }
    176   1.1     rmind 
    177  1.11     rmind /*
    178  1.11     rmind  * npf_ifmap_getid: get the ID for the given network interface.
    179  1.11     rmind  *
    180  1.11     rmind  * => This routine is typically called from the packet handler when
    181  1.11     rmind  *    matching whether the packet is on particular network interface.
    182  1.11     rmind  *
    183  1.11     rmind  * => This routine is lock-free; if the NPF configuration is flushed
    184  1.11     rmind  *    while the packet is in-flight, the ID will not match because we
    185  1.11     rmind  *    keep the IDs linear.
    186  1.11     rmind  */
    187  1.11     rmind unsigned
    188   1.7  christos npf_ifmap_getid(npf_t *npf, const ifnet_t *ifp)
    189   1.1     rmind {
    190  1.13     rmind 	const unsigned id = (uintptr_t)npf->ifops->getmeta(npf, ifp);
    191  1.11     rmind 	return id;
    192   1.1     rmind }
    193   1.1     rmind 
    194   1.8  christos /*
    195  1.12     rmind  * npf_ifmap_copylogname: this function is toxic; it can return garbage
    196  1.12     rmind  * as we don't lock, but it is only used temporarily and only for logging.
    197   1.8  christos  */
    198   1.8  christos void
    199  1.11     rmind npf_ifmap_copylogname(npf_t *npf, unsigned id, char *buf, size_t len)
    200   1.8  christos {
    201  1.12     rmind 	const unsigned i = NPF_IFMAP_ID2SLOT(npf, id);
    202  1.12     rmind 
    203  1.12     rmind 	membar_consumer();
    204  1.11     rmind 
    205  1.12     rmind 	if (id != NPF_IFMAP_NOID && i < NPF_MAX_IFMAP) {
    206  1.11     rmind 		/*
    207  1.11     rmind 		 * Lock-free access is safe as there is an extra byte
    208  1.11     rmind 		 * with a permanent NUL terminator at the end.
    209  1.11     rmind 		 */
    210  1.12     rmind 		const npf_ifmap_t *ifmap = &npf->ifmap[i];
    211  1.11     rmind 		strlcpy(buf, ifmap->ifname, MIN(len, IFNAMSIZ));
    212  1.11     rmind 	} else {
    213   1.8  christos 		strlcpy(buf, "???", len);
    214  1.11     rmind 	}
    215   1.8  christos }
    216   1.8  christos 
    217  1.11     rmind void
    218  1.11     rmind npf_ifmap_copyname(npf_t *npf, unsigned id, char *buf, size_t len)
    219   1.4     rmind {
    220  1.11     rmind 	mutex_enter(&npf->ifmap_lock);
    221  1.11     rmind 	npf_ifmap_copylogname(npf, id, buf, len);
    222  1.11     rmind 	mutex_exit(&npf->ifmap_lock);
    223   1.4     rmind }
    224   1.4     rmind 
    225   1.7  christos __dso_public void
    226  1.10     rmind npfk_ifmap_attach(npf_t *npf, ifnet_t *ifp)
    227   1.1     rmind {
    228   1.7  christos 	const npf_ifops_t *ifops = npf->ifops;
    229  1.11     rmind 	unsigned id;
    230   1.7  christos 
    231  1.11     rmind 	mutex_enter(&npf->ifmap_lock);
    232  1.13     rmind 	id = npf_ifmap_lookup(npf, ifops->getname(npf, ifp));
    233  1.13     rmind 	ifops->setmeta(npf, ifp, (void *)(uintptr_t)id);
    234  1.11     rmind 	mutex_exit(&npf->ifmap_lock);
    235   1.1     rmind }
    236   1.1     rmind 
    237   1.7  christos __dso_public void
    238  1.10     rmind npfk_ifmap_detach(npf_t *npf, ifnet_t *ifp)
    239   1.1     rmind {
    240   1.5     rmind 	/* Diagnostic. */
    241  1.11     rmind 	mutex_enter(&npf->ifmap_lock);
    242  1.13     rmind 	npf->ifops->setmeta(npf, ifp, (void *)(uintptr_t)NPF_IFMAP_NOID);
    243  1.11     rmind 	mutex_exit(&npf->ifmap_lock);
    244   1.1     rmind }
    245