freebsd-nq/sys/netinet/ip_gre.c
Andrey V. Elsukov 6d8fdfa9d5 Rework IP encapsulation handling code.
Currently it has several disadvantages:
- it uses single mutex to protect internal structures. It is used by
  data- and control- path, thus there are no parallelism at all.
- it uses single list to keep encap handlers for both INET and INET6
  families.
- struct encaptab keeps unneeded information (src, dst, masks, protosw),
  that isn't used by code in the source tree.
- matches are prioritized and when many tunneling interfaces are
  registered, encapcheck handler of each interface is invoked for each
  packet. The search takes O(n) for n interfaces. All this work is done
  with exclusive lock held.

What this patch includes:
- the datapath is converted to be lockless using epoch(9) KPI.
- struct encaptab now linked using CK_LIST.
- all unused fields removed from struct encaptab. Several new fields
  addedr: min_length is the minimum packet length, that encapsulation
  handler expects to see; exact_match is maximum number of bits, that
  can return an encapsulation handler, when it wants to consume a packet.
- IPv6 and IPv4 handlers are stored in separate lists;
- added new "encap_lookup_t" method, that will be used later. It is
  targeted to speedup lookup of needed interface, when gif(4)/gre(4) have
  many interfaces.
- the need to use protosw structure is eliminated. The only pr_input
  method was used from this structure, so I don't see the need to keep
  using it.
- encap_input_t method changed to avoid using mbuf tags to store softc
  pointer. Now it is passed directly trough encap_input_t method.
  encap_getarg() funtions is removed.
- all sockaddr structures and code that uses them removed. We don't have
  any code in the tree that uses them. All consumers use encap_attach_func()
  method, that relies on invoking of encapcheck() to determine the needed
  handler.
- introduced struct encap_config, it contains parameters of encap handler
  that is going to be registered by encap_attach() function.
- encap handlers are stored in lists ordered by exact_match value, thus
  handlers that need more bits to match will be checked first, and if
  encapcheck method returns exact_match value, the search will be stopped.
- all current consumers changed to use new KPI.

Reviewed by:	mmacy
Sponsored by:	Yandex LLC
Differential Revision:	https://reviews.freebsd.org/D15617
2018-06-05 20:51:01 +00:00

155 lines
4.4 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause-NetBSD
*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* Copyright (c) 2014 Andrey V. Elsukov <ae@FreeBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Heiko W.Rupp <hwr@pilhuhn.de>
*
* IPv6-over-GRE contributed by Gert Doering <gert@greenie.muc.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $NetBSD: ip_gre.c,v 1.29 2003/09/05 23:02:43 itojun Exp $
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
#include "opt_inet6.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/rmlock.h>
#include <sys/sysctl.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/vnet.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/ip_encap.h>
#include <netinet/ip_var.h>
#ifdef INET6
#include <netinet/ip6.h>
#endif
#include <net/if_gre.h>
#define GRE_TTL 30
VNET_DEFINE(int, ip_gre_ttl) = GRE_TTL;
#define V_ip_gre_ttl VNET(ip_gre_ttl)
SYSCTL_INT(_net_inet_ip, OID_AUTO, grettl, CTLFLAG_VNET | CTLFLAG_RW,
&VNET_NAME(ip_gre_ttl), 0, "Default TTL value for encapsulated packets");
static int
in_gre_encapcheck(const struct mbuf *m, int off, int proto, void *arg)
{
GRE_RLOCK_TRACKER;
struct gre_softc *sc;
struct ip *ip;
sc = (struct gre_softc *)arg;
if ((GRE2IFP(sc)->if_flags & IFF_UP) == 0)
return (0);
M_ASSERTPKTHDR(m);
GRE_RLOCK(sc);
if (sc->gre_family == 0)
goto bad;
KASSERT(sc->gre_family == AF_INET,
("wrong gre_family: %d", sc->gre_family));
ip = mtod(m, struct ip *);
if (sc->gre_oip.ip_src.s_addr != ip->ip_dst.s_addr ||
sc->gre_oip.ip_dst.s_addr != ip->ip_src.s_addr)
goto bad;
GRE_RUNLOCK(sc);
return (32 * 3); /* src + dst + gre_hdr */
bad:
GRE_RUNLOCK(sc);
return (0);
}
int
in_gre_output(struct mbuf *m, int af, int hlen)
{
struct greip *gi;
gi = mtod(m, struct greip *);
switch (af) {
case AF_INET:
/*
* gre_transmit() has used M_PREPEND() that doesn't guarantee
* m_data is contiguous more than hlen bytes. Use m_copydata()
* here to avoid m_pullup().
*/
m_copydata(m, hlen + offsetof(struct ip, ip_tos),
sizeof(u_char), &gi->gi_ip.ip_tos);
m_copydata(m, hlen + offsetof(struct ip, ip_id),
sizeof(u_short), (caddr_t)&gi->gi_ip.ip_id);
break;
#ifdef INET6
case AF_INET6:
gi->gi_ip.ip_tos = 0; /* XXX */
ip_fillid(&gi->gi_ip);
break;
#endif
}
gi->gi_ip.ip_ttl = V_ip_gre_ttl;
gi->gi_ip.ip_len = htons(m->m_pkthdr.len);
return (ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL));
}
static const struct encap_config ipv4_encap_cfg = {
.proto = IPPROTO_GRE,
.min_length = sizeof(struct greip) + sizeof(struct ip),
.exact_match = (sizeof(in_addr_t) << 4) + 32,
.check = in_gre_encapcheck,
.input = gre_input
};
int
in_gre_attach(struct gre_softc *sc)
{
KASSERT(sc->gre_ecookie == NULL, ("gre_ecookie isn't NULL"));
sc->gre_ecookie = ip_encap_attach(&ipv4_encap_cfg, sc, M_WAITOK);
return (0);
}