Changeset View
Standalone View
sys/net/if_vxlan.c
/*- | /*- | ||||
* Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org> | * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org> | ||||
* All rights reserved. | * All rights reserved. | ||||
* Copyright (c) 2020, Chelsio Communications. | |||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice unmodified, this list of conditions, and the following | * notice unmodified, this list of conditions, and the following | ||||
* disclaimer. | * disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | |||||
#include <net/if.h> | #include <net/if.h> | ||||
#include <net/if_var.h> | #include <net/if_var.h> | ||||
#include <net/if_clone.h> | #include <net/if_clone.h> | ||||
#include <net/if_dl.h> | #include <net/if_dl.h> | ||||
#include <net/if_media.h> | #include <net/if_media.h> | ||||
#include <net/if_types.h> | #include <net/if_types.h> | ||||
#include <net/if_vxlan.h> | #include <net/if_vxlan.h> | ||||
#include <net/netisr.h> | #include <net/netisr.h> | ||||
#include <net/route.h> | |||||
#include <net/route/nhop.h> | |||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netinet/in_systm.h> | #include <netinet/in_systm.h> | ||||
#include <netinet/in_var.h> | #include <netinet/in_var.h> | ||||
#include <netinet/in_pcb.h> | #include <netinet/in_pcb.h> | ||||
#include <netinet/ip.h> | #include <netinet/ip.h> | ||||
#include <netinet/ip6.h> | #include <netinet/ip6.h> | ||||
#include <netinet/ip_var.h> | #include <netinet/ip_var.h> | ||||
#include <netinet/udp.h> | #include <netinet/udp.h> | ||||
#include <netinet/udp_var.h> | #include <netinet/udp_var.h> | ||||
#include <netinet/in_fib.h> | |||||
#include <netinet6/in6_fib.h> | |||||
#include <netinet6/ip6_var.h> | #include <netinet6/ip6_var.h> | ||||
#include <netinet6/scope6_var.h> | #include <netinet6/scope6_var.h> | ||||
struct vxlan_softc; | struct vxlan_softc; | ||||
LIST_HEAD(vxlan_softc_head, vxlan_softc); | LIST_HEAD(vxlan_softc_head, vxlan_softc); | ||||
struct vxlan_socket_mc_info { | struct vxlan_socket_mc_info { | ||||
union vxlan_sockaddr vxlsomc_saddr; | union vxlan_sockaddr vxlsomc_saddr; | ||||
union vxlan_sockaddr vxlsomc_gaddr; | union vxlan_sockaddr vxlsomc_gaddr; | ||||
int vxlsomc_ifidx; | int vxlsomc_ifidx; | ||||
int vxlsomc_users; | int vxlsomc_users; | ||||
}; | }; | ||||
/* | /* | ||||
* The maximum MTU of encapsulated ethernet frame within IPv4/UDP packet. | * The maximum MTU of encapsulated ethernet frame within IPv4/UDP packet. | ||||
*/ | */ | ||||
#define VXLAN_MAX_MTU (IP_MAXPACKET - \ | #define VXLAN_MAX_MTU (IP_MAXPACKET - \ | ||||
60 /* Maximum IPv4 header len */ - \ | 60 /* Maximum IPv4 header len */ - \ | ||||
sizeof(struct udphdr) - \ | sizeof(struct udphdr) - \ | ||||
sizeof(struct vxlan_header) - \ | sizeof(struct vxlan_header) - \ | ||||
ETHER_HDR_LEN - ETHER_CRC_LEN - ETHER_VLAN_ENCAP_LEN) | ETHER_HDR_LEN - ETHER_CRC_LEN - ETHER_VLAN_ENCAP_LEN) | ||||
#define VXLAN_BASIC_IFCAPS (IFCAP_LINKSTATE | IFCAP_JUMBO_MTU) | |||||
#define VXLAN_SO_MC_MAX_GROUPS 32 | #define VXLAN_SO_MC_MAX_GROUPS 32 | ||||
#define VXLAN_SO_VNI_HASH_SHIFT 6 | #define VXLAN_SO_VNI_HASH_SHIFT 6 | ||||
#define VXLAN_SO_VNI_HASH_SIZE (1 << VXLAN_SO_VNI_HASH_SHIFT) | #define VXLAN_SO_VNI_HASH_SIZE (1 << VXLAN_SO_VNI_HASH_SHIFT) | ||||
#define VXLAN_SO_VNI_HASH(_vni) ((_vni) % VXLAN_SO_VNI_HASH_SIZE) | #define VXLAN_SO_VNI_HASH(_vni) ((_vni) % VXLAN_SO_VNI_HASH_SIZE) | ||||
struct vxlan_socket { | struct vxlan_socket { | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
struct vxlan_statistics { | struct vxlan_statistics { | ||||
uint32_t ftable_nospace; | uint32_t ftable_nospace; | ||||
uint32_t ftable_lock_upgrade_failed; | uint32_t ftable_lock_upgrade_failed; | ||||
}; | }; | ||||
struct vxlan_softc { | struct vxlan_softc { | ||||
struct ifnet *vxl_ifp; | struct ifnet *vxl_ifp; | ||||
int vxl_reqcap; | |||||
struct vxlan_socket *vxl_sock; | struct vxlan_socket *vxl_sock; | ||||
uint32_t vxl_vni; | uint32_t vxl_vni; | ||||
union vxlan_sockaddr vxl_src_addr; | union vxlan_sockaddr vxl_src_addr; | ||||
union vxlan_sockaddr vxl_dst_addr; | union vxlan_sockaddr vxl_dst_addr; | ||||
uint32_t vxl_flags; | uint32_t vxl_flags; | ||||
#define VXLAN_FLAG_INIT 0x0001 | #define VXLAN_FLAG_INIT 0x0001 | ||||
#define VXLAN_FLAG_TEARDOWN 0x0002 | #define VXLAN_FLAG_TEARDOWN 0x0002 | ||||
#define VXLAN_FLAG_LEARN 0x0004 | #define VXLAN_FLAG_LEARN 0x0004 | ||||
▲ Show 20 Lines • Show All 131 Lines • ▼ Show 20 Lines | |||||
static struct ifnet * | static struct ifnet * | ||||
vxlan_multicast_if_ref(struct vxlan_softc *, int); | vxlan_multicast_if_ref(struct vxlan_softc *, int); | ||||
static void vxlan_free_multicast(struct vxlan_softc *); | static void vxlan_free_multicast(struct vxlan_softc *); | ||||
static int vxlan_setup_multicast_interface(struct vxlan_softc *); | static int vxlan_setup_multicast_interface(struct vxlan_softc *); | ||||
static int vxlan_setup_multicast(struct vxlan_softc *); | static int vxlan_setup_multicast(struct vxlan_softc *); | ||||
static int vxlan_setup_socket(struct vxlan_softc *); | static int vxlan_setup_socket(struct vxlan_softc *); | ||||
static void vxlan_setup_interface(struct vxlan_softc *); | #ifdef INET6 | ||||
static void vxlan_setup_zero_checksum_port(struct vxlan_softc *); | |||||
#endif | |||||
static void vxlan_setup_interface_hdrlen(struct vxlan_softc *); | |||||
static int vxlan_valid_init_config(struct vxlan_softc *); | static int vxlan_valid_init_config(struct vxlan_softc *); | ||||
static void vxlan_init_wait(struct vxlan_softc *); | static void vxlan_init_wait(struct vxlan_softc *); | ||||
static void vxlan_init_complete(struct vxlan_softc *); | static void vxlan_init_complete(struct vxlan_softc *); | ||||
static void vxlan_init(void *); | static void vxlan_init(void *); | ||||
static void vxlan_release(struct vxlan_softc *); | static void vxlan_release(struct vxlan_softc *); | ||||
static void vxlan_teardown_wait(struct vxlan_softc *); | static void vxlan_teardown_wait(struct vxlan_softc *); | ||||
static void vxlan_teardown_complete(struct vxlan_softc *); | static void vxlan_teardown_complete(struct vxlan_softc *); | ||||
static void vxlan_teardown_locked(struct vxlan_softc *); | static void vxlan_teardown_locked(struct vxlan_softc *); | ||||
Show All 36 Lines | |||||
static void vxlan_rcv_udp_packet(struct mbuf *, int, struct inpcb *, | static void vxlan_rcv_udp_packet(struct mbuf *, int, struct inpcb *, | ||||
const struct sockaddr *, void *); | const struct sockaddr *, void *); | ||||
static int vxlan_input(struct vxlan_socket *, uint32_t, struct mbuf **, | static int vxlan_input(struct vxlan_socket *, uint32_t, struct mbuf **, | ||||
const struct sockaddr *); | const struct sockaddr *); | ||||
static void vxlan_set_default_config(struct vxlan_softc *); | static void vxlan_set_default_config(struct vxlan_softc *); | ||||
static int vxlan_set_user_config(struct vxlan_softc *, | static int vxlan_set_user_config(struct vxlan_softc *, | ||||
struct ifvxlanparam *); | struct ifvxlanparam *); | ||||
static void vxlan_set_hwcaps(struct vxlan_softc *); | |||||
static int vxlan_clone_create(struct if_clone *, int, caddr_t); | static int vxlan_clone_create(struct if_clone *, int, caddr_t); | ||||
static void vxlan_clone_destroy(struct ifnet *); | static void vxlan_clone_destroy(struct ifnet *); | ||||
static uint32_t vxlan_mac_hash(struct vxlan_softc *, const uint8_t *); | static uint32_t vxlan_mac_hash(struct vxlan_softc *, const uint8_t *); | ||||
static int vxlan_media_change(struct ifnet *); | static int vxlan_media_change(struct ifnet *); | ||||
static void vxlan_media_status(struct ifnet *, struct ifmediareq *); | static void vxlan_media_status(struct ifnet *, struct ifmediareq *); | ||||
static int vxlan_sockaddr_cmp(const union vxlan_sockaddr *, | static int vxlan_sockaddr_cmp(const union vxlan_sockaddr *, | ||||
▲ Show 20 Lines • Show All 1,189 Lines • ▼ Show 20 Lines | if (vso != NULL) { | ||||
if (multicast != 0) | if (multicast != 0) | ||||
vxlan_free_multicast(sc); | vxlan_free_multicast(sc); | ||||
vxlan_socket_release(vso); | vxlan_socket_release(vso); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
#ifdef INET6 | |||||
static void | static void | ||||
vxlan_setup_interface(struct vxlan_softc *sc) | vxlan_setup_zero_checksum_port(struct vxlan_softc *sc) | ||||
{ | { | ||||
if (!VXLAN_SOCKADDR_IS_IPV6(&sc->vxl_src_addr)) | |||||
return; | |||||
if (V_zero_checksum_port != 0) | |||||
return; /* Leave it alone. */ | |||||
MPASS(sc->vxl_src_addr.in6.sin6_port != 0); | |||||
MPASS(sc->vxl_dst_addr.in6.sin6_port != 0); | |||||
if (sc->vxl_src_addr.in6.sin6_port != sc->vxl_dst_addr.in6.sin6_port) | |||||
return; | |||||
kib: Does this need a reason string ? | |||||
Done Inline ActionsYes, that's a good idea. I added if_printf's for all cases where we wanted to set the zero checksum port but couldn't. np: Yes, that's a good idea. I added if_printf's for all cases where we wanted to set the zero… | |||||
V_zero_checksum_port = ntohs(sc->vxl_src_addr.in6.sin6_port); | |||||
printf("rfc6935_port set to %d\n", V_zero_checksum_port); | |||||
Done Inline ActionsIt should be if_printf. kib: It should be if_printf. | |||||
} | |||||
#endif | |||||
static void | |||||
vxlan_setup_interface_hdrlen(struct vxlan_softc *sc) | |||||
{ | |||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
ifp = sc->vxl_ifp; | ifp = sc->vxl_ifp; | ||||
ifp->if_hdrlen = ETHER_HDR_LEN + sizeof(struct vxlanudphdr); | ifp->if_hdrlen = ETHER_HDR_LEN + sizeof(struct vxlanudphdr); | ||||
if (VXLAN_SOCKADDR_IS_IPV4(&sc->vxl_dst_addr) != 0) | if (VXLAN_SOCKADDR_IS_IPV4(&sc->vxl_dst_addr) != 0) | ||||
ifp->if_hdrlen += sizeof(struct ip); | ifp->if_hdrlen += sizeof(struct ip); | ||||
else if (VXLAN_SOCKADDR_IS_IPV6(&sc->vxl_dst_addr) != 0) | else if (VXLAN_SOCKADDR_IS_IPV6(&sc->vxl_dst_addr) != 0) | ||||
▲ Show 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | ||||
return; | return; | ||||
} | } | ||||
sc->vxl_flags |= VXLAN_FLAG_INIT; | sc->vxl_flags |= VXLAN_FLAG_INIT; | ||||
VXLAN_WUNLOCK(sc); | VXLAN_WUNLOCK(sc); | ||||
if (vxlan_valid_init_config(sc) != 0) | if (vxlan_valid_init_config(sc) != 0) | ||||
goto out; | goto out; | ||||
vxlan_setup_interface(sc); | |||||
if (vxlan_setup_socket(sc) != 0) | if (vxlan_setup_socket(sc) != 0) | ||||
goto out; | goto out; | ||||
#ifdef INET6 | |||||
vxlan_setup_zero_checksum_port(sc); | |||||
#endif | |||||
/* Initialize the default forwarding entry. */ | /* Initialize the default forwarding entry. */ | ||||
vxlan_ftable_entry_init(sc, &sc->vxl_default_fe, empty_mac, | vxlan_ftable_entry_init(sc, &sc->vxl_default_fe, empty_mac, | ||||
&sc->vxl_dst_addr.sa, VXLAN_FE_FLAG_STATIC); | &sc->vxl_dst_addr.sa, VXLAN_FE_FLAG_STATIC); | ||||
VXLAN_WLOCK(sc); | VXLAN_WLOCK(sc); | ||||
ifp->if_drv_flags |= IFF_DRV_RUNNING; | ifp->if_drv_flags |= IFF_DRV_RUNNING; | ||||
callout_reset(&sc->vxl_callout, vxlan_ftable_prune_period * hz, | callout_reset(&sc->vxl_callout, vxlan_ftable_prune_period * hz, | ||||
vxlan_timer, sc); | vxlan_timer, sc); | ||||
VXLAN_WUNLOCK(sc); | VXLAN_WUNLOCK(sc); | ||||
if_link_state_change(ifp, LINK_STATE_UP); | if_link_state_change(ifp, LINK_STATE_UP); | ||||
EVENTHANDLER_INVOKE(vxlan_start, ifp, sc->vxl_src_addr.in4.sin_family, | |||||
Not Done Inline ActionsI do not quite understand what this locking achieve. It serializes calls into driver, but driver can do that internally as well. We discussed that all ups and downs for vxlan should be mutually excluded to avoid reordering of events as seen by driver. kib: I do not quite understand what this locking achieve. It serializes calls into driver, but… | |||||
ntohs(sc->vxl_src_addr.in4.sin_port)); | |||||
Not Done Inline ActionsThere seems to be absent any synchronization between vxlan_start invocations, same for vxlan_stop. I see that you added refcount to maintain tcam entry liveness in the driver, but I suspect that we already have a problem at the higher level. I believe that we want much more strict interface there, in the simplest case we can have global sx that protects all vxlan configuration actions. kib: There seems to be absent any synchronization between vxlan_start invocations, same for… | |||||
Done Inline ActionsI originally wanted to generate these events during vxlan create/destroy but the port can be changed when the vxlan interface is down. I don't think changing the VNI, addresses, or ports after create is a common use case so I'd even be ok with freezing them on create. start/stop could then be invoked in create/destroy and wouldn't need a lock. What do you (and others who may be reading this) think? Otherwise I'll add a global sx. np: I originally wanted to generate these events during vxlan create/destroy but the port can be… | |||||
Not Done Inline ActionsI do not follow. Are you suggesting that there would be no events to driver on up/down of the vxlan child interface ? I am not sure we can handle it (we might be able, but I am not sure). Unsynchronized events could cause driver to see paradoxical updates, like your vxlan_refcounter underflow. kib: I do not follow. Are you suggesting that there would be no events to driver on up/down of the… | |||||
Done Inline ActionsThe events will become vxlan_create/destroy rather that vxlan_start/stop and will be generated exactly once per vxlan interface rather than on every up and down. create/destroy do not race like start/stop so we wouldn't need a lock. But this requires the port to be immutable after create and that's not the case today. The driver would do exactly what it does in the start/stop events right now. np: The events will become vxlan_create/destroy rather that vxlan_start/stop and will be generated… | |||||
out: | out: | ||||
vxlan_init_complete(sc); | vxlan_init_complete(sc); | ||||
} | } | ||||
static void | static void | ||||
vxlan_release(struct vxlan_softc *sc) | vxlan_release(struct vxlan_softc *sc) | ||||
{ | { | ||||
Show All 40 Lines | vxlan_teardown_locked(struct vxlan_softc *sc) | ||||
ifp->if_flags &= ~IFF_UP; | ifp->if_flags &= ~IFF_UP; | ||||
ifp->if_drv_flags &= ~IFF_DRV_RUNNING; | ifp->if_drv_flags &= ~IFF_DRV_RUNNING; | ||||
callout_stop(&sc->vxl_callout); | callout_stop(&sc->vxl_callout); | ||||
vso = sc->vxl_sock; | vso = sc->vxl_sock; | ||||
sc->vxl_sock = NULL; | sc->vxl_sock = NULL; | ||||
VXLAN_WUNLOCK(sc); | VXLAN_WUNLOCK(sc); | ||||
if_link_state_change(ifp, LINK_STATE_DOWN); | if_link_state_change(ifp, LINK_STATE_DOWN); | ||||
EVENTHANDLER_INVOKE(vxlan_stop, ifp, sc->vxl_src_addr.in4.sin_family, | |||||
ntohs(sc->vxl_src_addr.in4.sin_port)); | |||||
if (vso != NULL) { | if (vso != NULL) { | ||||
vxlan_socket_remove_softc(vso, sc); | vxlan_socket_remove_softc(vso, sc); | ||||
if (sc->vxl_vso_mc_index != -1) { | if (sc->vxl_vso_mc_index != -1) { | ||||
vxlan_socket_mc_release_group_by_idx(vso, | vxlan_socket_mc_release_group_by_idx(vso, | ||||
sc->vxl_vso_mc_index); | sc->vxl_vso_mc_index); | ||||
sc->vxl_vso_mc_index = -1; | sc->vxl_vso_mc_index = -1; | ||||
▲ Show 20 Lines • Show All 153 Lines • ▼ Show 20 Lines | if (VXLAN_SOCKADDR_IS_IPV6(vxlsa)) { | ||||
error = vxlan_sockaddr_in6_embedscope(vxlsa); | error = vxlan_sockaddr_in6_embedscope(vxlsa); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
} | } | ||||
VXLAN_WLOCK(sc); | VXLAN_WLOCK(sc); | ||||
if (vxlan_can_change_config(sc)) { | if (vxlan_can_change_config(sc)) { | ||||
vxlan_sockaddr_in_copy(&sc->vxl_src_addr, &vxlsa->sa); | vxlan_sockaddr_in_copy(&sc->vxl_src_addr, &vxlsa->sa); | ||||
vxlan_set_hwcaps(sc); | |||||
error = 0; | error = 0; | ||||
} else | } else | ||||
error = EBUSY; | error = EBUSY; | ||||
VXLAN_WUNLOCK(sc); | VXLAN_WUNLOCK(sc); | ||||
return (error); | return (error); | ||||
} | } | ||||
Show All 13 Lines | if (VXLAN_SOCKADDR_IS_IPV6(vxlsa)) { | ||||
error = vxlan_sockaddr_in6_embedscope(vxlsa); | error = vxlan_sockaddr_in6_embedscope(vxlsa); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
} | } | ||||
VXLAN_WLOCK(sc); | VXLAN_WLOCK(sc); | ||||
if (vxlan_can_change_config(sc)) { | if (vxlan_can_change_config(sc)) { | ||||
vxlan_sockaddr_in_copy(&sc->vxl_dst_addr, &vxlsa->sa); | vxlan_sockaddr_in_copy(&sc->vxl_dst_addr, &vxlsa->sa); | ||||
vxlan_setup_interface_hdrlen(sc); | |||||
error = 0; | error = 0; | ||||
} else | } else | ||||
error = EBUSY; | error = EBUSY; | ||||
VXLAN_WUNLOCK(sc); | VXLAN_WUNLOCK(sc); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | vxlan_ctrl_set_multicast_if(struct vxlan_softc * sc, void *arg) | ||||
struct ifvxlancmd *cmd; | struct ifvxlancmd *cmd; | ||||
int error; | int error; | ||||
cmd = arg; | cmd = arg; | ||||
VXLAN_WLOCK(sc); | VXLAN_WLOCK(sc); | ||||
if (vxlan_can_change_config(sc)) { | if (vxlan_can_change_config(sc)) { | ||||
strlcpy(sc->vxl_mc_ifname, cmd->vxlcmd_ifname, IFNAMSIZ); | strlcpy(sc->vxl_mc_ifname, cmd->vxlcmd_ifname, IFNAMSIZ); | ||||
vxlan_set_hwcaps(sc); | |||||
error = 0; | error = 0; | ||||
} else | } else | ||||
error = EBUSY; | error = EBUSY; | ||||
VXLAN_WUNLOCK(sc); | VXLAN_WUNLOCK(sc); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 205 Lines • ▼ Show 20 Lines | vxlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) | ||||
case SIOCSIFMTU: | case SIOCSIFMTU: | ||||
if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > VXLAN_MAX_MTU) | if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > VXLAN_MAX_MTU) | ||||
error = EINVAL; | error = EINVAL; | ||||
else | else | ||||
ifp->if_mtu = ifr->ifr_mtu; | ifp->if_mtu = ifr->ifr_mtu; | ||||
break; | break; | ||||
case SIOCSIFCAP: | |||||
VXLAN_WLOCK(sc); | |||||
sc->vxl_reqcap = ifr->ifr_reqcap; | |||||
vxlan_set_hwcaps(sc); | |||||
VXLAN_WUNLOCK(sc); | |||||
break; | |||||
default: | default: | ||||
error = ether_ioctl(ifp, cmd, data); | error = ether_ioctl(ifp, cmd, data); | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
Show All 35 Lines | vxlan_encap_header(struct vxlan_softc *sc, struct mbuf *m, int ipoff, | ||||
udph->uh_sum = 0; | udph->uh_sum = 0; | ||||
vxh = &hdr->vxlh_hdr; | vxh = &hdr->vxlh_hdr; | ||||
vxh->vxlh_flags = htonl(VXLAN_HDR_FLAGS_VALID_VNI); | vxh->vxlh_flags = htonl(VXLAN_HDR_FLAGS_VALID_VNI); | ||||
vxh->vxlh_vni = htonl(sc->vxl_vni << VXLAN_HDR_VNI_SHIFT); | vxh->vxlh_vni = htonl(sc->vxl_vni << VXLAN_HDR_VNI_SHIFT); | ||||
} | } | ||||
#endif | #endif | ||||
/* | |||||
* Return the CSUM_INNER_* equivalent of CSUM_* caps. | |||||
*/ | |||||
static uint32_t | |||||
csum_flags_to_inner_flags(uint32_t csum_flags_in, uint32_t encap) | |||||
{ | |||||
uint32_t csum_flags = CSUM_ENCAP_VXLAN; | |||||
const uint32_t v4 = CSUM_IP | CSUM_IP_UDP | CSUM_IP_TCP; | |||||
/* | |||||
* csum_flags can request either v4 or v6 offload but not both. | |||||
* tcp_output always sets CSUM_TSO (both CSUM_IP_TSO and CSUM_IP6_TSO) | |||||
* so those bits are no good to detect the IP version. Other bits are | |||||
* always set with CSUM_TSO and we use those to figure out the IP | |||||
* version. | |||||
*/ | |||||
if (csum_flags_in & v4) { | |||||
if (csum_flags_in & CSUM_IP) | |||||
csum_flags |= CSUM_INNER_IP; | |||||
if (csum_flags_in & CSUM_IP_UDP) | |||||
csum_flags |= CSUM_INNER_IP_UDP; | |||||
if (csum_flags_in & CSUM_IP_TCP) | |||||
csum_flags |= CSUM_INNER_IP_TCP; | |||||
if (csum_flags_in & CSUM_IP_TSO) | |||||
csum_flags |= CSUM_INNER_IP_TSO; | |||||
} else { | |||||
#ifdef INVARIANTS | |||||
const uint32_t v6 = CSUM_IP6_UDP | CSUM_IP6_TCP; | |||||
MPASS((csum_flags_in & v6) != 0); | |||||
#endif | |||||
if (csum_flags_in & CSUM_IP6_UDP) | |||||
csum_flags |= CSUM_INNER_IP6_UDP; | |||||
if (csum_flags_in & CSUM_IP6_TCP) | |||||
csum_flags |= CSUM_INNER_IP6_TCP; | |||||
if (csum_flags_in & CSUM_IP6_TSO) | |||||
csum_flags |= CSUM_INNER_IP6_TSO; | |||||
} | |||||
return (csum_flags); | |||||
} | |||||
static int | static int | ||||
vxlan_encap4(struct vxlan_softc *sc, const union vxlan_sockaddr *fvxlsa, | vxlan_encap4(struct vxlan_softc *sc, const union vxlan_sockaddr *fvxlsa, | ||||
struct mbuf *m) | struct mbuf *m) | ||||
{ | { | ||||
#ifdef INET | #ifdef INET | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct ip *ip; | struct ip *ip; | ||||
struct in_addr srcaddr, dstaddr; | struct in_addr srcaddr, dstaddr; | ||||
uint16_t srcport, dstport; | uint16_t srcport, dstport; | ||||
int len, mcast, error; | int len, mcast, error; | ||||
struct route route, *ro; | |||||
struct sockaddr_in *sin; | |||||
uint32_t csum_flags; | |||||
NET_EPOCH_ASSERT(); | |||||
ifp = sc->vxl_ifp; | ifp = sc->vxl_ifp; | ||||
srcaddr = sc->vxl_src_addr.in4.sin_addr; | srcaddr = sc->vxl_src_addr.in4.sin_addr; | ||||
srcport = vxlan_pick_source_port(sc, m); | srcport = vxlan_pick_source_port(sc, m); | ||||
dstaddr = fvxlsa->in4.sin_addr; | dstaddr = fvxlsa->in4.sin_addr; | ||||
dstport = fvxlsa->in4.sin_port; | dstport = fvxlsa->in4.sin_port; | ||||
M_PREPEND(m, sizeof(struct ip) + sizeof(struct vxlanudphdr), | M_PREPEND(m, sizeof(struct ip) + sizeof(struct vxlanudphdr), | ||||
M_NOWAIT); | M_NOWAIT); | ||||
Show All 14 Lines | #ifdef INET | ||||
ip->ip_src = srcaddr; | ip->ip_src = srcaddr; | ||||
ip->ip_dst = dstaddr; | ip->ip_dst = dstaddr; | ||||
vxlan_encap_header(sc, m, sizeof(struct ip), srcport, dstport); | vxlan_encap_header(sc, m, sizeof(struct ip), srcport, dstport); | ||||
mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1 : 0; | mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1 : 0; | ||||
m->m_flags &= ~(M_MCAST | M_BCAST); | m->m_flags &= ~(M_MCAST | M_BCAST); | ||||
error = ip_output(m, NULL, NULL, 0, sc->vxl_im4o, NULL); | m->m_pkthdr.csum_flags &= CSUM_FLAGS_TX; | ||||
if (m->m_pkthdr.csum_flags != 0) { | |||||
/* | |||||
* HW checksum (L3 and/or L4) or TSO has been requested. Look | |||||
* up the ifnet for the outbound route and verify that the | |||||
* outbound ifnet can perform the requested operation on the | |||||
* inner frame. | |||||
*/ | |||||
bzero(&route, sizeof(route)); | |||||
ro = &route; | |||||
sin = (struct sockaddr_in *)&ro->ro_dst; | |||||
sin->sin_family = AF_INET; | |||||
sin->sin_len = sizeof(*sin); | |||||
sin->sin_addr = ip->ip_dst; | |||||
ro->ro_nh = fib4_lookup(RT_DEFAULT_FIB, ip->ip_dst, 0, NHR_NONE, | |||||
0); | |||||
if (ro->ro_nh == NULL) { | |||||
m_freem(m); | |||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | |||||
return (EHOSTUNREACH); | |||||
} | |||||
csum_flags = csum_flags_to_inner_flags(m->m_pkthdr.csum_flags, | |||||
CSUM_ENCAP_VXLAN); | |||||
if ((csum_flags & ro->ro_nh->nh_ifp->if_hwassist) != | |||||
csum_flags) { | |||||
printf("m->csum_flags 0x%08x, csum_flags 0x%08x, " | |||||
Done Inline ActionsShould this printf be rate-limited ? And again, perhaps if_printf for the nexthop ifnet ? Perhaps we should add a counter for specific error, instead of potentially flooding printf ? kib: Should this printf be rate-limited ? And again, perhaps if_printf for the nexthop ifnet ? | |||||
Done Inline ActionsThis is leftover debug. There is value in displaying the caps that mismatched so I changed this to a rate limited if_printf. np: This is leftover debug. There is value in displaying the caps that mismatched so I changed… | |||||
"hwassist 0x%08x, missing 0x%08x\n", | |||||
m->m_pkthdr.csum_flags, csum_flags, | |||||
(uint32_t)ro->ro_nh->nh_ifp->if_hwassist, | |||||
csum_flags & | |||||
~(uint32_t)ro->ro_nh->nh_ifp->if_hwassist); | |||||
m_freem(m); | |||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | |||||
return (ENXIO); | |||||
} | |||||
m->m_pkthdr.csum_flags = csum_flags; | |||||
} else | |||||
ro = NULL; | |||||
error = ip_output(m, NULL, ro, 0, sc->vxl_im4o, NULL); | |||||
if (error == 0) { | if (error == 0) { | ||||
if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); | if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); | ||||
if_inc_counter(ifp, IFCOUNTER_OBYTES, len); | if_inc_counter(ifp, IFCOUNTER_OBYTES, len); | ||||
if (mcast != 0) | if (mcast != 0) | ||||
if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); | if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); | ||||
} else | } else | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | ||||
Show All 9 Lines | vxlan_encap6(struct vxlan_softc *sc, const union vxlan_sockaddr *fvxlsa, | ||||
struct mbuf *m) | struct mbuf *m) | ||||
{ | { | ||||
#ifdef INET6 | #ifdef INET6 | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct ip6_hdr *ip6; | struct ip6_hdr *ip6; | ||||
const struct in6_addr *srcaddr, *dstaddr; | const struct in6_addr *srcaddr, *dstaddr; | ||||
uint16_t srcport, dstport; | uint16_t srcport, dstport; | ||||
int len, mcast, error; | int len, mcast, error; | ||||
struct route_in6 route, *ro; | |||||
struct sockaddr_in6 *sin6; | |||||
uint32_t csum_flags; | |||||
NET_EPOCH_ASSERT(); | |||||
ifp = sc->vxl_ifp; | ifp = sc->vxl_ifp; | ||||
srcaddr = &sc->vxl_src_addr.in6.sin6_addr; | srcaddr = &sc->vxl_src_addr.in6.sin6_addr; | ||||
srcport = vxlan_pick_source_port(sc, m); | srcport = vxlan_pick_source_port(sc, m); | ||||
dstaddr = &fvxlsa->in6.sin6_addr; | dstaddr = &fvxlsa->in6.sin6_addr; | ||||
dstport = fvxlsa->in6.sin6_port; | dstport = fvxlsa->in6.sin6_port; | ||||
M_PREPEND(m, sizeof(struct ip6_hdr) + sizeof(struct vxlanudphdr), | M_PREPEND(m, sizeof(struct ip6_hdr) + sizeof(struct vxlanudphdr), | ||||
M_NOWAIT); | M_NOWAIT); | ||||
Show All 10 Lines | #ifdef INET6 | ||||
ip6->ip6_plen = 0; | ip6->ip6_plen = 0; | ||||
ip6->ip6_nxt = IPPROTO_UDP; | ip6->ip6_nxt = IPPROTO_UDP; | ||||
ip6->ip6_hlim = sc->vxl_ttl; | ip6->ip6_hlim = sc->vxl_ttl; | ||||
ip6->ip6_src = *srcaddr; | ip6->ip6_src = *srcaddr; | ||||
ip6->ip6_dst = *dstaddr; | ip6->ip6_dst = *dstaddr; | ||||
vxlan_encap_header(sc, m, sizeof(struct ip6_hdr), srcport, dstport); | vxlan_encap_header(sc, m, sizeof(struct ip6_hdr), srcport, dstport); | ||||
mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1 : 0; | |||||
m->m_flags &= ~(M_MCAST | M_BCAST); | |||||
ro = NULL; | |||||
m->m_pkthdr.csum_flags &= CSUM_FLAGS_TX; | |||||
if (m->m_pkthdr.csum_flags != 0) { | |||||
/* | /* | ||||
* XXX BMV We need support for RFC6935 before we can send and | * HW checksum (L3 and/or L4) or TSO has been requested. Look | ||||
* receive IPv6 UDP packets with a zero checksum. | * up the ifnet for the outbound route and verify that the | ||||
* outbound ifnet can perform the requested operation on the | |||||
* inner frame. | |||||
*/ | */ | ||||
{ | bzero(&route, sizeof(route)); | ||||
ro = &route; | |||||
sin6 = (struct sockaddr_in6 *)&ro->ro_dst; | |||||
sin6->sin6_family = AF_INET6; | |||||
sin6->sin6_len = sizeof(*sin6); | |||||
sin6->sin6_addr = ip6->ip6_dst; | |||||
ro->ro_nh = fib6_lookup(RT_DEFAULT_FIB, &ip6->ip6_dst, 0, | |||||
NHR_NONE, 0); | |||||
if (ro->ro_nh == NULL) { | |||||
m_freem(m); | |||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | |||||
return (EHOSTUNREACH); | |||||
} | |||||
csum_flags = csum_flags_to_inner_flags(m->m_pkthdr.csum_flags, | |||||
CSUM_ENCAP_VXLAN); | |||||
if ((csum_flags & ro->ro_nh->nh_ifp->if_hwassist) != | |||||
csum_flags) { | |||||
printf("m->csum_flags 0x%08x, csum_flags 0x%08x, " | |||||
"hwassist 0x%08x, missing 0x%08x\n", | |||||
m->m_pkthdr.csum_flags, csum_flags, | |||||
(uint32_t)ro->ro_nh->nh_ifp->if_hwassist, | |||||
csum_flags & | |||||
~(uint32_t)ro->ro_nh->nh_ifp->if_hwassist); | |||||
m_freem(m); | |||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | |||||
return (ENXIO); | |||||
} | |||||
m->m_pkthdr.csum_flags = csum_flags; | |||||
} else if (ntohs(dstport) != V_zero_checksum_port) { | |||||
struct udphdr *hdr = mtodo(m, sizeof(struct ip6_hdr)); | struct udphdr *hdr = mtodo(m, sizeof(struct ip6_hdr)); | ||||
hdr->uh_sum = in6_cksum_pseudo(ip6, | hdr->uh_sum = in6_cksum_pseudo(ip6, | ||||
m->m_pkthdr.len - sizeof(struct ip6_hdr), IPPROTO_UDP, 0); | m->m_pkthdr.len - sizeof(struct ip6_hdr), IPPROTO_UDP, 0); | ||||
m->m_pkthdr.csum_flags = CSUM_UDP_IPV6; | m->m_pkthdr.csum_flags = CSUM_UDP_IPV6; | ||||
m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); | m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); | ||||
} | } | ||||
error = ip6_output(m, NULL, ro, 0, sc->vxl_im6o, NULL, NULL); | |||||
mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1 : 0; | |||||
m->m_flags &= ~(M_MCAST | M_BCAST); | |||||
error = ip6_output(m, NULL, NULL, 0, sc->vxl_im6o, NULL, NULL); | |||||
if (error == 0) { | if (error == 0) { | ||||
if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); | if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); | ||||
if_inc_counter(ifp, IFCOUNTER_OBYTES, len); | if_inc_counter(ifp, IFCOUNTER_OBYTES, len); | ||||
if (mcast != 0) | if (mcast != 0) | ||||
if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); | if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); | ||||
} else | } else | ||||
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); | ||||
▲ Show 20 Lines • Show All 132 Lines • ▼ Show 20 Lines | vxlan_input(struct vxlan_socket *vso, uint32_t vni, struct mbuf **m0, | ||||
} | } | ||||
if (sc->vxl_flags & VXLAN_FLAG_LEARN) | if (sc->vxl_flags & VXLAN_FLAG_LEARN) | ||||
vxlan_ftable_learn(sc, sa, eh->ether_shost); | vxlan_ftable_learn(sc, sa, eh->ether_shost); | ||||
m_clrprotoflags(m); | m_clrprotoflags(m); | ||||
m->m_pkthdr.rcvif = ifp; | m->m_pkthdr.rcvif = ifp; | ||||
M_SETFIB(m, ifp->if_fib); | M_SETFIB(m, ifp->if_fib); | ||||
if (ifp->if_capenable & IFCAP_RXCSUM && | |||||
m->m_pkthdr.csum_flags & CSUM_ENCAP_VXLAN) { | |||||
uint32_t csum_flags = 0; | |||||
error = netisr_queue_src(NETISR_ETHER, 0, m); | if (m->m_pkthdr.csum_flags & CSUM_INNER_L3_CALC) | ||||
csum_flags |= CSUM_L3_CALC; | |||||
if (m->m_pkthdr.csum_flags & CSUM_INNER_L3_VALID) | |||||
csum_flags |= CSUM_L3_VALID; | |||||
if (m->m_pkthdr.csum_flags & CSUM_INNER_L4_CALC) | |||||
csum_flags |= CSUM_L4_CALC; | |||||
if (m->m_pkthdr.csum_flags & CSUM_INNER_L4_VALID) | |||||
csum_flags |= CSUM_L4_VALID; | |||||
m->m_pkthdr.csum_flags = csum_flags; | |||||
} else { | |||||
/* clear everything */ | |||||
m->m_pkthdr.csum_flags = 0; | |||||
m->m_pkthdr.csum_data = 0; | |||||
} | |||||
error = netisr_dispatch(NETISR_ETHER, m); | |||||
*m0 = NULL; | *m0 = NULL; | ||||
out: | out: | ||||
vxlan_release(sc); | vxlan_release(sc); | ||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | #endif | ||||
if (vxlp->vxlp_with & VXLAN_PARAM_WITH_LEARN) { | if (vxlp->vxlp_with & VXLAN_PARAM_WITH_LEARN) { | ||||
if (vxlp->vxlp_learn == 0) | if (vxlp->vxlp_learn == 0) | ||||
sc->vxl_flags &= ~VXLAN_FLAG_LEARN; | sc->vxl_flags &= ~VXLAN_FLAG_LEARN; | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | |||||
* A VXLAN interface inherits the capabilities of the vxlandev or the interface | |||||
* hosting the vxlanlocal address. | |||||
*/ | |||||
static void | |||||
vxlan_set_hwcaps(struct vxlan_softc *sc) | |||||
{ | |||||
struct epoch_tracker et; | |||||
struct ifnet *p; | |||||
struct ifaddr *ifa; | |||||
u_long hwa; | |||||
int cap, ena; | |||||
bool rel; | |||||
struct ifnet *ifp = sc->vxl_ifp; | |||||
/* reset caps */ | |||||
ifp->if_capabilities &= VXLAN_BASIC_IFCAPS; | |||||
ifp->if_capenable &= VXLAN_BASIC_IFCAPS; | |||||
ifp->if_hwassist = 0; | |||||
NET_EPOCH_ENTER(et); | |||||
CURVNET_SET(ifp->if_vnet); | |||||
rel = false; | |||||
p = NULL; | |||||
if (sc->vxl_mc_ifname[0] != '\0') { | |||||
rel = true; | |||||
p = ifunit_ref(sc->vxl_mc_ifname); | |||||
} else if (vxlan_sockaddr_in_any(&sc->vxl_src_addr) == 0) { | |||||
if (sc->vxl_src_addr.sa.sa_family == AF_INET) { | |||||
struct sockaddr_in in4 = sc->vxl_src_addr.in4; | |||||
in4.sin_port = 0; | |||||
ifa = ifa_ifwithaddr((struct sockaddr *)&in4); | |||||
if (ifa != NULL) | |||||
p = ifa->ifa_ifp; | |||||
} else if (sc->vxl_src_addr.sa.sa_family == AF_INET6) { | |||||
struct sockaddr_in6 in6 = sc->vxl_src_addr.in6; | |||||
in6.sin6_port = 0; | |||||
ifa = ifa_ifwithaddr((struct sockaddr *)&in6); | |||||
if (ifa != NULL) | |||||
p = ifa->ifa_ifp; | |||||
} | |||||
} | |||||
if (p == NULL) | |||||
goto done; | |||||
cap = ena = hwa = 0; | |||||
/* checksum offload */ | |||||
if (p->if_capabilities & IFCAP_VXLAN_HWCSUM) | |||||
cap |= p->if_capabilities & (IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6); | |||||
if (p->if_capenable & IFCAP_VXLAN_HWCSUM) { | |||||
ena |= sc->vxl_reqcap & p->if_capenable & | |||||
(IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6); | |||||
if (ena & IFCAP_TXCSUM) { | |||||
if (p->if_hwassist & CSUM_INNER_IP) | |||||
hwa |= CSUM_IP; | |||||
if (p->if_hwassist & CSUM_INNER_IP_UDP) | |||||
hwa |= CSUM_IP_UDP; | |||||
if (p->if_hwassist & CSUM_INNER_IP_TCP) | |||||
hwa |= CSUM_IP_TCP; | |||||
} | |||||
if (ena & IFCAP_TXCSUM_IPV6) { | |||||
if (p->if_hwassist & CSUM_INNER_IP6_UDP) | |||||
hwa |= CSUM_IP6_UDP; | |||||
if (p->if_hwassist & CSUM_INNER_IP6_TCP) | |||||
hwa |= CSUM_IP6_TCP; | |||||
} | |||||
} | |||||
/* hardware TSO */ | |||||
if (p->if_capabilities & IFCAP_VXLAN_HWTSO) { | |||||
cap |= p->if_capabilities & IFCAP_TSO; | |||||
if (p->if_hw_tsomax > IP_MAXPACKET - ifp->if_hdrlen) | |||||
ifp->if_hw_tsomax = IP_MAXPACKET - ifp->if_hdrlen; | |||||
else | |||||
ifp->if_hw_tsomax = p->if_hw_tsomax; | |||||
/* XXX: tsomaxsegcount decrement is cxgbe specific */ | |||||
ifp->if_hw_tsomaxsegcount = p->if_hw_tsomaxsegcount - 1; | |||||
ifp->if_hw_tsomaxsegsize = p->if_hw_tsomaxsegsize; | |||||
} | |||||
if (p->if_capenable & IFCAP_VXLAN_HWTSO) { | |||||
ena |= sc->vxl_reqcap & p->if_capenable & IFCAP_TSO; | |||||
if (ena & IFCAP_TSO) { | |||||
if (p->if_hwassist & CSUM_INNER_IP_TSO) | |||||
hwa |= CSUM_IP_TSO; | |||||
if (p->if_hwassist & CSUM_INNER_IP6_TSO) | |||||
hwa |= CSUM_IP6_TSO; | |||||
} | |||||
} | |||||
ifp->if_capabilities |= cap; | |||||
ifp->if_capenable |= ena; | |||||
ifp->if_hwassist |= hwa; | |||||
if (rel) | |||||
if_rele(p); | |||||
done: | |||||
CURVNET_RESTORE(); | |||||
NET_EPOCH_EXIT(et); | |||||
} | |||||
static int | static int | ||||
vxlan_clone_create(struct if_clone *ifc, int unit, caddr_t params) | vxlan_clone_create(struct if_clone *ifc, int unit, caddr_t params) | ||||
{ | { | ||||
struct vxlan_softc *sc; | struct vxlan_softc *sc; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct ifvxlanparam vxlp; | struct ifvxlanparam vxlp; | ||||
int error; | int error; | ||||
Show All 27 Lines | vxlan_clone_create(struct if_clone *ifc, int unit, caddr_t params) | ||||
ifp->if_softc = sc; | ifp->if_softc = sc; | ||||
if_initname(ifp, vxlan_name, unit); | if_initname(ifp, vxlan_name, unit); | ||||
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | ||||
ifp->if_init = vxlan_init; | ifp->if_init = vxlan_init; | ||||
ifp->if_ioctl = vxlan_ioctl; | ifp->if_ioctl = vxlan_ioctl; | ||||
ifp->if_transmit = vxlan_transmit; | ifp->if_transmit = vxlan_transmit; | ||||
ifp->if_qflush = vxlan_qflush; | ifp->if_qflush = vxlan_qflush; | ||||
ifp->if_capabilities |= IFCAP_LINKSTATE | IFCAP_JUMBO_MTU; | ifp->if_capabilities = VXLAN_BASIC_IFCAPS; | ||||
ifp->if_capenable |= IFCAP_LINKSTATE | IFCAP_JUMBO_MTU; | ifp->if_capenable = VXLAN_BASIC_IFCAPS; | ||||
sc->vxl_reqcap = -1; | |||||
vxlan_set_hwcaps(sc); | |||||
ifmedia_init(&sc->vxl_media, 0, vxlan_media_change, vxlan_media_status); | ifmedia_init(&sc->vxl_media, 0, vxlan_media_change, vxlan_media_status); | ||||
ifmedia_add(&sc->vxl_media, IFM_ETHER | IFM_AUTO, 0, NULL); | ifmedia_add(&sc->vxl_media, IFM_ETHER | IFM_AUTO, 0, NULL); | ||||
ifmedia_set(&sc->vxl_media, IFM_ETHER | IFM_AUTO); | ifmedia_set(&sc->vxl_media, IFM_ETHER | IFM_AUTO); | ||||
ether_gen_addr(ifp, &sc->vxl_hwaddr); | ether_gen_addr(ifp, &sc->vxl_hwaddr); | ||||
ether_ifattach(ifp, sc->vxl_hwaddr.octet); | ether_ifattach(ifp, sc->vxl_hwaddr.octet); | ||||
ifp->if_baudrate = 0; | ifp->if_baudrate = 0; | ||||
ifp->if_hdrlen = 0; | vxlan_setup_interface_hdrlen(sc); | ||||
return (0); | return (0); | ||||
fail: | fail: | ||||
free(sc, M_VXLAN); | free(sc, M_VXLAN); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 407 Lines • Show Last 20 Lines |
Does this need a reason string ?