From f1f552f625b9d625468551168d8ab1fb2f99723b Mon Sep 17 00:00:00 2001 From: Jeffrey Hsu Date: Sun, 24 Aug 2003 23:07:08 +0000 Subject: [PATCH] Add support for Protocol Independent Multicast. Submitted to FreeBSD by: Pavlin Radoslavov --- sys/conf/options | 3 +- sys/config/LINT | 6 +- sys/i386/conf/LINT | 6 +- sys/kern/uipc_socket.c | 9 +- sys/net/ip_mroute/ip_mroute.c | 1459 ++++++++++++++++++++++++++++- sys/net/ip_mroute/ip_mroute.h | 163 +++- sys/netgraph/ksocket/ng_ksocket.c | 5 +- sys/netinet/in.h | 20 +- sys/netinet/in_proto.c | 20 +- sys/netinet/ip_output.c | 295 +++--- sys/netinet/ip_var.h | 6 +- sys/netinet/pim.h | 118 +++ sys/netinet/pim_var.h | 81 ++ sys/netinet/raw_ip.c | 12 +- sys/sys/socketvar.h | 6 +- 15 files changed, 2015 insertions(+), 194 deletions(-) create mode 100644 sys/netinet/pim.h create mode 100644 sys/netinet/pim_var.h diff --git a/sys/conf/options b/sys/conf/options index 7cb1004a9d..8d276bc1cf 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -1,5 +1,5 @@ # $FreeBSD: src/sys/conf/options,v 1.191.2.53 2003/06/04 17:56:58 sam Exp $ -# $DragonFly: src/sys/conf/options,v 1.2 2003/06/17 04:28:20 dillon Exp $ +# $DragonFly: src/sys/conf/options,v 1.3 2003/08/24 23:07:07 hsu Exp $ # # On the handling of kernel options # @@ -260,6 +260,7 @@ ETHER_8023 opt_ef.h ETHER_8022 opt_ef.h ETHER_SNAP opt_ef.h MROUTING opt_mrouting.h +PIM opt_mrouting.h INET opt_inet.h INET6 opt_inet6.h IPSEC opt_ipsec.h diff --git a/sys/config/LINT b/sys/config/LINT index e33db0db4a..ce0e899cbc 100644 --- a/sys/config/LINT +++ b/sys/config/LINT @@ -3,7 +3,7 @@ # as much of the source tree as it can. # # $FreeBSD: src/sys/i386/conf/LINT,v 1.749.2.144 2003/06/04 17:56:59 sam Exp $ -# $DragonFly: src/sys/config/LINT,v 1.4 2003/08/07 21:17:20 dillon Exp $ +# $DragonFly: src/sys/config/LINT,v 1.5 2003/08/24 23:07:07 hsu Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -567,6 +567,9 @@ pseudo-device stf #6to4 IPv6 over IPv4 encapsulation # MROUTING enables the kernel multicast packet forwarder, which works # with mrouted(8). # +# PIM enables Protocol Independent Multicast in the kernel. +# Requires MROUTING enabled. +# # IPFIREWALL enables support for IP firewall construction, in # conjunction with the `ipfw' program. IPFIREWALL_VERBOSE sends # logged packets to the system logger. IPFIREWALL_VERBOSE_LIMIT @@ -596,6 +599,7 @@ pseudo-device stf #6to4 IPv6 over IPv4 encapsulation # TCPDEBUG is undocumented. # options MROUTING # Multicast routing +options PIM # Protocol Independent Multicast options IPFIREWALL #firewall options IPFIREWALL_VERBOSE #enable logging to syslogd(8) options IPFIREWALL_FORWARD #enable transparent proxy support diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT index 904deca8f4..812955999e 100644 --- a/sys/i386/conf/LINT +++ b/sys/i386/conf/LINT @@ -3,7 +3,7 @@ # as much of the source tree as it can. # # $FreeBSD: src/sys/i386/conf/LINT,v 1.749.2.144 2003/06/04 17:56:59 sam Exp $ -# $DragonFly: src/sys/i386/conf/Attic/LINT,v 1.4 2003/08/07 21:17:20 dillon Exp $ +# $DragonFly: src/sys/i386/conf/Attic/LINT,v 1.5 2003/08/24 23:07:07 hsu Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -567,6 +567,9 @@ pseudo-device stf #6to4 IPv6 over IPv4 encapsulation # MROUTING enables the kernel multicast packet forwarder, which works # with mrouted(8). # +# PIM enables Protocol Independent Multicast in the kernel. +# Requires MROUTING enabled. +# # IPFIREWALL enables support for IP firewall construction, in # conjunction with the `ipfw' program. IPFIREWALL_VERBOSE sends # logged packets to the system logger. IPFIREWALL_VERBOSE_LIMIT @@ -596,6 +599,7 @@ pseudo-device stf #6to4 IPv6 over IPv4 encapsulation # TCPDEBUG is undocumented. # options MROUTING # Multicast routing +options PIM # Protocol Independent Multicast options IPFIREWALL #firewall options IPFIREWALL_VERBOSE #enable logging to syslogd(8) options IPFIREWALL_FORWARD #enable transparent proxy support diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index 116e24d119..92ee1b7932 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -31,8 +31,8 @@ * SUCH DAMAGE. * * @(#)uipc_socket.c 8.3 (Berkeley) 4/15/94 - * $FreeBSD: src/sys/kern/uipc_socket.c,v 1.68.2.22 2002/12/15 09:24:23 maxim Exp $ - * $DragonFly: src/sys/kern/uipc_socket.c,v 1.8 2003/07/26 19:42:11 rob Exp $ + * $FreeBSD: src/sys/kern/uipc_socket.c,v 1.68.2.23 2003/08/24 08:24:38 hsu Exp $ + * $DragonFly: src/sys/kern/uipc_socket.c,v 1.9 2003/08/24 23:07:07 hsu Exp $ */ #include "opt_inet.h" @@ -1233,10 +1233,7 @@ bad: /* Helper routine for getsockopt */ int -sooptcopyout(sopt, buf, len) - struct sockopt *sopt; - void *buf; - size_t len; +sooptcopyout(struct sockopt *sopt, const void *buf, size_t len) { int error; size_t valsize; diff --git a/sys/net/ip_mroute/ip_mroute.c b/sys/net/ip_mroute/ip_mroute.c index fa7447c96f..6661fc1f3b 100644 --- a/sys/net/ip_mroute/ip_mroute.c +++ b/sys/net/ip_mroute/ip_mroute.c @@ -7,15 +7,27 @@ * Modified by Van Jacobson, LBL, January 1993 * Modified by Ajit Thyagarajan, PARC, August 1993 * Modified by Bill Fenner, PARC, April 1995 + * Modified by Ahmed Helmy, SGI, June 1996 + * Modified by George Edmond Eddy (Rusty), ISI, February 1998 + * Modified by Pavlin Radoslavov, USC/ISI, May 1998, August 1999, October 2000 + * Modified by Hitoshi Asaeda, WIDE, August 2000 + * Modified by Pavlin Radoslavov, ICSI, October 2002 * * MROUTING Revision: 3.5 - * $FreeBSD: src/sys/netinet/ip_mroute.c,v 1.56.2.7 2003/01/23 21:06:45 sam Exp $ - * $DragonFly: src/sys/net/ip_mroute/ip_mroute.c,v 1.3 2003/08/07 21:17:29 dillon Exp $ + * and PIM-SMv2 and PIM-DM support, advanced API support, + * bandwidth metering and signaling + * + * $FreeBSD: src/sys/netinet/ip_mroute.c,v 1.56.2.10 2003/08/24 21:37:34 hsu Exp $ + * $DragonFly: src/sys/net/ip_mroute/ip_mroute.c,v 1.4 2003/08/24 23:07:07 hsu Exp $ */ #include "opt_mrouting.h" #include "opt_random_ip_id.h" +#ifdef PIM +#define _PIM_VT 1 +#endif + #include #include #include @@ -37,6 +49,10 @@ #include #include "ip_mroute.h" #include +#ifdef PIM +#include +#include +#endif #include #include @@ -52,6 +68,9 @@ static u_int mrtdebug; /* any set of the flags below */ #define DEBUG_FORWARD 0x04 #define DEBUG_EXPIRE 0x08 #define DEBUG_XMIT 0x10 +#define DEBUG_PIM 0x20 + +#define VIFI_INVALID ((vifi_t) -1) #define M_HASCL(m) ((m)->m_flags & M_EXT) @@ -63,8 +82,16 @@ SYSCTL_STRUCT(_net_inet_ip, OID_AUTO, mrtstat, CTLFLAG_RW, "Multicast Routing Statistics (struct mrtstat, netinet/ip_mroute.h)"); static struct mfc *mfctable[MFCTBLSIZ]; -static u_char nexpire[MFCTBLSIZ]; +SYSCTL_OPAQUE(_net_inet_ip, OID_AUTO, mfctable, CTLFLAG_RD, + &mfctable, sizeof(mfctable), "S,*mfc[MFCTBLSIZ]", + "Multicast Forwarding Table (struct *mfc[MFCTBLSIZ], netinet/ip_mroute.h)"); + static struct vif viftable[MAXVIFS]; +SYSCTL_OPAQUE(_net_inet_ip, OID_AUTO, viftable, CTLFLAG_RD, + &viftable, sizeof(viftable), "S,vif[MAXVIFS]", + "Multicast Virtual Interfaces (struct vif[MAXVIFS], netinet/ip_mroute.h)"); + +static u_char nexpire[MFCTBLSIZ]; static struct callout_handle expire_upcalls_ch; @@ -107,6 +134,80 @@ static struct ip multicast_encap_iphdr = { 0, /* checksum */ }; +/* + * Bandwidth meter variables and constants + */ +static MALLOC_DEFINE(M_BWMETER, "bwmeter", "multicast upcall bw meters"); +/* + * Pending timeouts are stored in a hash table, the key being the + * expiration time. Periodically, the entries are analysed and processed. + */ +#define BW_METER_BUCKETS 1024 +static struct bw_meter *bw_meter_timers[BW_METER_BUCKETS]; +static struct callout_handle bw_meter_ch; +#define BW_METER_PERIOD (hz) /* periodical handling of bw meters */ + +/* + * Pending upcalls are stored in a vector which is flushed when + * full, or periodically + */ +static struct bw_upcall bw_upcalls[BW_UPCALLS_MAX]; +static u_int bw_upcalls_n; /* # of pending upcalls */ +static struct callout_handle bw_upcalls_ch; +#define BW_UPCALLS_PERIOD (hz) /* periodical flush of bw upcalls */ + +#ifdef PIM +static struct pimstat pimstat; +SYSCTL_STRUCT(_net_inet_pim, PIMCTL_STATS, stats, CTLFLAG_RD, + &pimstat, pimstat, + "PIM Statistics (struct pimstat, netinet/pim_var.h)"); + +/* + * Note: the PIM Register encapsulation adds the following in front of a + * data packet: + * + * struct pim_encap_hdr { + * struct ip ip; + * struct pim_encap_pimhdr pim; + * } + * + */ + +struct pim_encap_pimhdr { + struct pim pim; + uint32_t flags; +}; + +static struct ip pim_encap_iphdr = { +#if BYTE_ORDER == LITTLE_ENDIAN + sizeof(struct ip) >> 2, + IPVERSION, +#else + IPVERSION, + sizeof(struct ip) >> 2, +#endif + 0, /* tos */ + sizeof(struct ip), /* total length */ + 0, /* id */ + 0, /* frag offset */ + ENCAP_TTL, + IPPROTO_PIM, + 0, /* checksum */ +}; + +static struct pim_encap_pimhdr pim_encap_pimhdr = { + { + PIM_MAKE_VT(PIM_VERSION, PIM_REGISTER), /* PIM vers and message type */ + 0, /* reserved */ + 0, /* checksum */ + }, + 0 /* flags */ +}; + +static struct ifnet multicast_register_if; +static vifi_t reg_vif_num = VIFI_INVALID; +#endif /* PIM */ + /* * Private variables. */ @@ -134,8 +235,9 @@ static int get_vif_cnt(struct sioc_vif_req *); static int ip_mrouter_init(struct socket *, int); static int add_vif(struct vifctl *); static int del_vif(vifi_t); -static int add_mfc(struct mfcctl *); -static int del_mfc(struct mfcctl *); +static int add_mfc(struct mfcctl2 *); +static int del_mfc(struct mfcctl2 *); +static int set_api_config(uint32_t *); /* chose API capabilities */ static int socket_send(struct socket *, struct mbuf *, struct sockaddr_in *); static int set_assert(int); static void expire_upcalls(void *); @@ -151,6 +253,32 @@ static void tbf_send_packet(struct vif *, struct mbuf *); static void tbf_update_tokens(struct vif *); static int priority(struct vif *, struct ip *); +/* + * Bandwidth monitoring + */ +static void free_bw_list(struct bw_meter *list); +static int add_bw_upcall(struct bw_upcall *); +static int del_bw_upcall(struct bw_upcall *); +static void bw_meter_receive_packet(struct bw_meter *x, int plen, + struct timeval *nowp); +static void bw_meter_prepare_upcall(struct bw_meter *x, struct timeval *nowp); +static void bw_upcalls_send(void); +static void schedule_bw_meter(struct bw_meter *x, struct timeval *nowp); +static void unschedule_bw_meter(struct bw_meter *x); +static void bw_meter_process(void); +static void expire_bw_upcalls_send(void *); +static void expire_bw_meter_process(void *); + +#ifdef PIM +static int pim_register_send(struct ip *, struct vif *, + struct mbuf *, struct mfc *); +static int pim_register_send_rp(struct ip *, struct vif *, + struct mbuf *, struct mfc *); +static int pim_register_send_upcall(struct ip *, struct vif *, + struct mbuf *, struct mfc *); +static struct mbuf *pim_register_prepare(struct ip *, struct mbuf *); +#endif + /* * whether or not special PIM assert processing is enabled. */ @@ -160,6 +288,17 @@ static int pim_assert; */ #define ASSERT_MSG_TIME 3000000 +/* + * Kernel multicast routing API capabilities and setup. + * If more API capabilities are added to the kernel, they should be + * recorded in `mrt_api_support'. + */ +static const uint32_t mrt_api_support = (MRT_MFC_FLAGS_DISABLE_WRONGVIF | + MRT_MFC_FLAGS_BORDER_VIF | + MRT_MFC_RP | + MRT_MFC_BW_UPCALL); +static uint32_t mrt_api_config = 0; + /* * Hash function for a source, group entry */ @@ -217,7 +356,9 @@ X_ip_mrouter_set(struct socket *so, struct sockopt *sopt) int error, optval; vifi_t vifi; struct vifctl vifc; - struct mfcctl mfc; + struct mfcctl2 mfc; + struct bw_upcall bw_upcall; + uint32_t i; if (so != ip_mrouter && sopt->sopt_name != MRT_INIT) return EPERM; @@ -251,7 +392,19 @@ X_ip_mrouter_set(struct socket *so, struct sockopt *sopt) case MRT_ADD_MFC: case MRT_DEL_MFC: - error = sooptcopyin(sopt, &mfc, sizeof mfc, sizeof mfc); + /* + * select data size depending on API version. + */ + if (sopt->sopt_name == MRT_ADD_MFC && + mrt_api_config & MRT_API_FLAGS_ALL) { + error = sooptcopyin(sopt, &mfc, sizeof(struct mfcctl2), + sizeof(struct mfcctl2)); + } else { + error = sooptcopyin(sopt, &mfc, sizeof(struct mfcctl), + sizeof(struct mfcctl)); + bzero((caddr_t)&mfc + sizeof(struct mfcctl), + sizeof(mfc) - sizeof(struct mfcctl)); + } if (error) break; if (sopt->sopt_name == MRT_ADD_MFC) @@ -267,6 +420,26 @@ X_ip_mrouter_set(struct socket *so, struct sockopt *sopt) set_assert(optval); break; + case MRT_API_CONFIG: + error = sooptcopyin(sopt, &i, sizeof i, sizeof i); + if (!error) + error = set_api_config(&i); + if (!error) + error = sooptcopyout(sopt, &i, sizeof i); + break; + + case MRT_ADD_BW_UPCALL: + case MRT_DEL_BW_UPCALL: + error = sooptcopyin(sopt, &bw_upcall, sizeof bw_upcall, + sizeof bw_upcall); + if (error) + break; + if (sopt->sopt_name == MRT_ADD_BW_UPCALL) + error = add_bw_upcall(&bw_upcall); + else + error = del_bw_upcall(&bw_upcall); + break; + default: error = EOPNOTSUPP; break; @@ -292,6 +465,14 @@ X_ip_mrouter_get(struct socket *so, struct sockopt *sopt) error = sooptcopyout(sopt, &pim_assert, sizeof pim_assert); break; + case MRT_API_SUPPORT: + error = sooptcopyout(sopt, &mrt_api_support, sizeof mrt_api_support); + break; + + case MRT_API_CONFIG: + error = sooptcopyout(sopt, &mrt_api_config, sizeof mrt_api_config); + break; + default: error = EOPNOTSUPP; break; @@ -392,6 +573,13 @@ ip_mrouter_init(struct socket *so, int version) expire_upcalls_ch = timeout(expire_upcalls, NULL, EXPIRE_TIMEOUT); + bw_upcalls_n = 0; + bzero((caddr_t)bw_meter_timers, sizeof(bw_meter_timers)); + bw_upcalls_ch = timeout(expire_bw_upcalls_send, NULL, BW_UPCALLS_PERIOD); + bw_meter_ch = timeout(expire_bw_meter_process, NULL, BW_METER_PERIOD); + + mrt_api_config = 0; + if (mrtdebug) log(LOG_DEBUG, "ip_mrouter_init\n"); @@ -420,7 +608,7 @@ X_ip_mrouter_done(void) */ for (vifi = 0; vifi < numvifs; vifi++) { if (viftable[vifi].v_lcl_addr.s_addr != 0 && - !(viftable[vifi].v_flags & VIFF_TUNNEL)) { + !(viftable[vifi].v_flags & (VIFF_TUNNEL | VIFF_REGISTER))) { struct sockaddr_in *so = (struct sockaddr_in *)&(ifr.ifr_addr); so->sin_len = sizeof(struct sockaddr_in); @@ -437,6 +625,11 @@ X_ip_mrouter_done(void) untimeout(expire_upcalls, NULL, expire_upcalls_ch); + mrt_api_config = 0; + bw_upcalls_n = 0; + untimeout(expire_bw_upcalls_send, NULL, bw_upcalls_ch); + untimeout(expire_bw_meter_process, NULL, bw_meter_ch); + /* * Free all multicast forwarding cache entries. */ @@ -451,6 +644,7 @@ X_ip_mrouter_done(void) free(rte, M_MRTABLE); rte = n; } + free_bw_list(rt->mfc_bw_meter); free(rt, M_MRTABLE); rt = nr; } @@ -458,11 +652,16 @@ X_ip_mrouter_done(void) bzero((caddr_t)mfctable, sizeof(mfctable)); + bzero(bw_meter_timers, sizeof(bw_meter_timers)); + /* * Reset de-encapsulation cache */ last_encap_src = INADDR_ANY; last_encap_vif = NULL; +#ifdef PIM + reg_vif_num = VIFI_INVALID; +#endif have_encap_tunnel = 0; ip_mrouter = NULL; @@ -489,6 +688,42 @@ set_assert(int i) return 0; } +/* + * Configure API capabilities + */ +int +set_api_config(uint32_t *apival) +{ + int i; + + /* + * We can set the API capabilities only if it is the first operation + * after MRT_INIT. I.e.: + * - there are no vifs installed + * - pim_assert is not enabled + * - the MFC table is empty + */ + if (numvifs > 0) { + *apival = 0; + return EPERM; + } + if (pim_assert) { + *apival = 0; + return EPERM; + } + for (i = 0; i < MFCTBLSIZ; i++) { + if (mfctable[i] != NULL) { + *apival = 0; + return EPERM; + } + } + + mrt_api_config = *apival & mrt_api_support; + *apival = mrt_api_config; + + return 0; +} + /* * Add a vif to the vif table */ @@ -510,11 +745,23 @@ add_vif(struct vifctl *vifcp) return EADDRNOTAVAIL; /* Find the interface with an address in AF_INET family */ - sin.sin_addr = vifcp->vifc_lcl_addr; - ifa = ifa_ifwithaddr((struct sockaddr *)&sin); - if (ifa == NULL) - return EADDRNOTAVAIL; - ifp = ifa->ifa_ifp; +#ifdef PIM + if (vifcp->vifc_flags & VIFF_REGISTER) { + /* + * XXX: Because VIFF_REGISTER does not really need a valid + * local interface (e.g. it could be 127.0.0.2), we don't + * check its address. + */ + ifp = NULL; + } else +#endif + { + sin.sin_addr = vifcp->vifc_lcl_addr; + ifa = ifa_ifwithaddr((struct sockaddr *)&sin); + if (ifa == NULL) + return EADDRNOTAVAIL; + ifp = ifa->ifa_ifp; + } if (vifcp->vifc_flags & VIFF_TUNNEL) { if ((vifcp->vifc_flags & VIFF_SRCRT) == 0) { @@ -541,6 +788,20 @@ add_vif(struct vifctl *vifcp) log(LOG_ERR, "source routed tunnels not supported\n"); return EOPNOTSUPP; } +#ifdef PIM + } else if (vifcp->vifc_flags & VIFF_REGISTER) { + ifp = &multicast_register_if; + if (mrtdebug) + log(LOG_DEBUG, "Adding a register vif, ifp: %p\n", + (void *)&multicast_register_if); + if (reg_vif_num == VIFI_INVALID) { + multicast_register_if.if_name = "register_vif"; + multicast_register_if.if_unit = 0; + multicast_register_if.if_flags = IFF_LOOPBACK; + bzero(&vifp->v_route, sizeof(vifp->v_route)); + reg_vif_num = vifcp->vifc_vifi; + } +#endif } else { /* Make sure the interface supports multicast */ if ((ifp->if_flags & IFF_MULTICAST) == 0) return EOPNOTSUPP; @@ -610,7 +871,7 @@ del_vif(vifi_t vifi) s = splnet(); - if (!(vifp->v_flags & VIFF_TUNNEL)) + if (!(vifp->v_flags & (VIFF_TUNNEL | VIFF_REGISTER))) if_allmulti(vifp->v_ifp, 0); if (vifp == last_encap_vif) { @@ -628,6 +889,11 @@ del_vif(vifi_t vifi) m_freem(m); } +#ifdef PIM + if (vifp->v_flags & VIFF_REGISTER) + reg_vif_num = VIFI_INVALID; +#endif + bzero((caddr_t)vifp->v_tbf, sizeof(*(vifp->v_tbf))); bzero((caddr_t)vifp, sizeof (*vifp)); @@ -649,20 +915,28 @@ del_vif(vifi_t vifi) * update an mfc entry without resetting counters and S,G addresses. */ static void -update_mfc_params(struct mfc *rt, struct mfcctl *mfccp) +update_mfc_params(struct mfc *rt, struct mfcctl2 *mfccp) { int i; rt->mfc_parent = mfccp->mfcc_parent; - for (i = 0; i < numvifs; i++) + for (i = 0; i < numvifs; i++) { rt->mfc_ttls[i] = mfccp->mfcc_ttls[i]; + rt->mfc_flags[i] = mfccp->mfcc_flags[i] & mrt_api_config & + MRT_MFC_FLAGS_ALL; + } + /* set the RP address */ + if (mrt_api_config & MRT_MFC_RP) + rt->mfc_rp = mfccp->mfcc_rp; + else + rt->mfc_rp.s_addr = INADDR_ANY; } /* * fully initialize an mfc entry from the parameter. */ static void -init_mfc_params(struct mfc *rt, struct mfcctl *mfccp) +init_mfc_params(struct mfc *rt, struct mfcctl2 *mfccp) { rt->mfc_origin = mfccp->mfcc_origin; rt->mfc_mcastgrp = mfccp->mfcc_mcastgrp; @@ -681,7 +955,7 @@ init_mfc_params(struct mfc *rt, struct mfcctl *mfccp) * Add an mfc entry */ static int -add_mfc(struct mfcctl *mfccp) +add_mfc(struct mfcctl2 *mfccp) { struct mfc *rt; u_long hash; @@ -778,6 +1052,7 @@ add_mfc(struct mfcctl *mfccp) rt->mfc_expire = 0; rt->mfc_stall = NULL; + rt->mfc_bw_meter = NULL; /* insert new entry at head of hash chain */ rt->mfc_next = mfctable[hash]; mfctable[hash] = rt; @@ -791,7 +1066,7 @@ add_mfc(struct mfcctl *mfccp) * Delete an mfc entry */ static int -del_mfc(struct mfcctl *mfccp) +del_mfc(struct mfcctl2 *mfccp) { struct in_addr origin; struct in_addr mcastgrp; @@ -799,6 +1074,7 @@ del_mfc(struct mfcctl *mfccp) struct mfc **nptr; u_long hash; int s; + struct bw_meter *list; origin = mfccp->mfcc_origin; mcastgrp = mfccp->mfcc_mcastgrp; @@ -821,10 +1097,19 @@ del_mfc(struct mfcctl *mfccp) } *nptr = rt->mfc_next; + + /* + * free the bw_meter entries + */ + list = rt->mfc_bw_meter; + rt->mfc_bw_meter = NULL; + free(rt, M_MRTABLE); splx(s); + free_bw_list(list); + return 0; } @@ -858,8 +1143,8 @@ socket_send(struct socket *s, struct mbuf *mm, struct sockaddr_in *src) #define TUNNEL_LEN 12 /* # bytes of IP option for tunnel encapsulation */ static int -X_ip_mforward(struct ip *ip, struct ifnet *ifp, - struct mbuf *m, struct ip_moptions *imo) +X_ip_mforward(struct ip *ip, struct ifnet *ifp, struct mbuf *m, + struct ip_moptions *imo) { struct mfc *rt; int s; @@ -874,7 +1159,7 @@ X_ip_mforward(struct ip *ip, struct ifnet *ifp, ((u_char *)(ip + 1))[1] != IPOPT_LSRR ) { /* * Packet arrived via a physical interface or - * an encapsulated tunnel. + * an encapsulated tunnel or a register_vif. */ } else { /* @@ -891,7 +1176,7 @@ X_ip_mforward(struct ip *ip, struct ifnet *ifp, return 1; } - if ((imo) && ((vifi = imo->imo_multicast_vif) < numvifs)) { + if (imo && ((vifi = imo->imo_multicast_vif) < numvifs)) { if (ip->ip_ttl < 255) ip->ip_ttl++; /* compensate for -1 in *_send routines */ if (rsvpdebug && ip->ip_p == IPPROTO_RSVP) { @@ -959,7 +1244,7 @@ X_ip_mforward(struct ip *ip, struct ifnet *ifp, splx(s); return ENOBUFS; } - mb0 = m_copy(m, 0, M_COPYALL); + mb0 = m_copypacket(m, M_DONTWAIT); if (mb0 && (M_HASCL(mb0) || mb0->m_len < hlen)) mb0 = m_pullup(mb0, hlen); if (mb0 == NULL) { @@ -987,7 +1272,7 @@ X_ip_mforward(struct ip *ip, struct ifnet *ifp, * Locate the vifi for the incoming interface for this packet. * If none found, drop packet. */ - for (vifi=0; vifi= numvifs) /* vif not found, drop packet */ goto non_fatal; @@ -1031,10 +1316,16 @@ fail: rt->mfc_mcastgrp.s_addr = ip->ip_dst.s_addr; rt->mfc_expire = UPCALL_EXPIRE; nexpire[hash]++; - for (i = 0; i < numvifs; i++) + for (i = 0; i < numvifs; i++) { rt->mfc_ttls[i] = 0; + rt->mfc_flags[i] = 0; + } rt->mfc_parent = -1; + rt->mfc_rp.s_addr = INADDR_ANY; /* clear the RP address */ + + rt->mfc_bw_meter = NULL; + /* link into table */ rt->mfc_next = mfctable[hash]; mfctable[hash] = rt; @@ -1118,6 +1409,16 @@ expire_upcalls(void *unused) ++mrtstat.mrts_cache_cleanups; nexpire[i]--; + /* + * free the bw_meter entries + */ + while (mfc->mfc_bw_meter != NULL) { + struct bw_meter *x = mfc->mfc_bw_meter; + + mfc->mfc_bw_meter = x->bm_mfc_next; + free(x, M_BWMETER); + } + *nptr = mfc->mfc_next; free(mfc, M_MRTABLE); } else { @@ -1144,11 +1445,11 @@ ip_mdq(struct mbuf *m, struct ifnet *ifp, struct mfc *rt, vifi_t xmt_vif) * input, they shouldn't get counted on output, so statistics keeping is * separate. */ -#define MC_SEND(ip,vifp,m) { \ - if ((vifp)->v_flags & VIFF_TUNNEL) \ - encap_send((ip), (vifp), (m)); \ - else \ - phyint_send((ip), (vifp), (m)); \ +#define MC_SEND(ip,vifp,m) { \ + if ((vifp)->v_flags & VIFF_TUNNEL) \ + encap_send((ip), (vifp), (m)); \ + else \ + phyint_send((ip), (vifp), (m)); \ } /* @@ -1157,6 +1458,11 @@ ip_mdq(struct mbuf *m, struct ifnet *ifp, struct mfc *rt, vifi_t xmt_vif) * (since vifi_t is u_short, -1 becomes MAXUSHORT, which > numvifs.) */ if (xmt_vif < numvifs) { +#ifdef PIM + if (viftable[xmt_vif].v_flags & VIFF_REGISTER) + pim_register_send(ip, viftable + xmt_vif, m, rt); + else +#endif MC_SEND(ip, viftable + xmt_vif, m); return 1; } @@ -1173,16 +1479,31 @@ ip_mdq(struct mbuf *m, struct ifnet *ifp, struct mfc *rt, vifi_t xmt_vif) ++mrtstat.mrts_wrong_if; ++rt->mfc_wrong_if; /* - * If we are doing PIM assert processing, and we are forwarding - * packets on this interface, and it is a broadcast medium - * interface (and not a tunnel), send a message to the routing daemon. + * If we are doing PIM assert processing, send a message + * to the routing daemon. + * + * XXX: A PIM-SM router needs the WRONGVIF detection so it + * can complete the SPT switch, regardless of the type + * of the iif (broadcast media, GRE tunnel, etc). */ - if (pim_assert && rt->mfc_ttls[vifi] && - (ifp->if_flags & IFF_BROADCAST) && - !(viftable[vifi].v_flags & VIFF_TUNNEL)) { + if (pim_assert && (vifi < numvifs) && viftable[vifi].v_ifp) { struct timeval now; u_long delta; +#ifdef PIM + if (ifp == &multicast_register_if) + pimstat.pims_rcv_registers_wrongiif++; +#endif + + /* Get vifi for the incoming packet */ + for (vifi=0; vifi < numvifs && viftable[vifi].v_ifp != ifp; vifi++) + ; + if (vifi >= numvifs) + return 0; /* The iif is not found: ignore the packet. */ + + if (rt->mfc_flags[vifi] & MRT_MFC_FLAGS_DISABLE_WRONGVIF) + return 0; /* WRONGVIF disabled: ignore the packet */ + GET_TIME(now); TV_DELTA(rt->mfc_last_assert, now, delta); @@ -1205,8 +1526,9 @@ ip_mdq(struct mbuf *m, struct ifnet *ifp, struct mfc *rt, vifi_t xmt_vif) im->im_mbz = 0; im->im_vif = vifi; - k_igmpsrc.sin_addr = im->im_src; + mrtstat.mrts_upcalls++; + k_igmpsrc.sin_addr = im->im_src; if (socket_send(ip_mrouter, mm, &k_igmpsrc) < 0) { log(LOG_WARNING, "ip_mforward: ip_mrouter socket queue full\n"); @@ -1239,9 +1561,26 @@ ip_mdq(struct mbuf *m, struct ifnet *ifp, struct mfc *rt, vifi_t xmt_vif) if ((rt->mfc_ttls[vifi] > 0) && (ip->ip_ttl > rt->mfc_ttls[vifi])) { viftable[vifi].v_pkt_out++; viftable[vifi].v_bytes_out += plen; +#ifdef PIM + if (viftable[vifi].v_flags & VIFF_REGISTER) + pim_register_send(ip, viftable + vifi, m, rt); + else +#endif MC_SEND(ip, viftable+vifi, m); } + /* + * Perform upcall-related bw measuring. + */ + if (rt->mfc_bw_meter != NULL) { + struct bw_meter *x; + struct timeval now; + + GET_TIME(now); + for (x = rt->mfc_bw_meter; x != NULL; x = x->bm_mfc_next) + bw_meter_receive_packet(x, plen, &now); + } + return 0; } @@ -1277,7 +1616,7 @@ phyint_send(struct ip *ip, struct vif *vifp, struct mbuf *m) * the IP header is actually copied, not just referenced, * so that ip_output() only scribbles on the copy. */ - mb_copy = m_copy(m, 0, M_COPYALL); + mb_copy = m_copypacket(m, M_DONTWAIT); if (mb_copy && (M_HASCL(mb_copy) || mb_copy->m_len < hlen)) mb_copy = m_pullup(mb_copy, hlen); if (mb_copy == NULL) @@ -1296,11 +1635,7 @@ encap_send(struct ip *ip, struct vif *vifp, struct mbuf *m) struct ip *ip_copy; int i, len = ip->ip_len; - /* - * XXX: take care of delayed checksums. - * XXX: if network interfaces are capable of computing checksum for - * encapsulated multicast data packets, we need to reconsider this. - */ + /* Take care of delayed checksums */ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { in_delayed_cksum(m); m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; @@ -1317,7 +1652,7 @@ encap_send(struct ip *ip, struct vif *vifp, struct mbuf *m) mb_copy->m_data += max_linkhdr; mb_copy->m_len = sizeof(multicast_encap_iphdr); - if ((mb_copy->m_next = m_copy(m, 0, M_COPYALL)) == NULL) { + if ((mb_copy->m_next = m_copypacket(m, M_DONTWAIT)) == NULL) { m_freem(mb_copy); return; } @@ -1854,6 +2189,1040 @@ X_rsvp_input(struct mbuf *m, int off, int proto) splx(s); } +/* + * Code for bandwidth monitors + */ + +/* + * Define common interface for timeval-related methods + */ +#define BW_TIMEVALCMP(tvp, uvp, cmp) timevalcmp((tvp), (uvp), cmp) +#define BW_TIMEVALDECR(vvp, uvp) timevalsub((vvp), (uvp)) +#define BW_TIMEVALADD(vvp, uvp) timevaladd((vvp), (uvp)) + +static uint32_t +compute_bw_meter_flags(struct bw_upcall *req) +{ + uint32_t flags = 0; + + if (req->bu_flags & BW_UPCALL_UNIT_PACKETS) + flags |= BW_METER_UNIT_PACKETS; + if (req->bu_flags & BW_UPCALL_UNIT_BYTES) + flags |= BW_METER_UNIT_BYTES; + if (req->bu_flags & BW_UPCALL_GEQ) + flags |= BW_METER_GEQ; + if (req->bu_flags & BW_UPCALL_LEQ) + flags |= BW_METER_LEQ; + + return flags; +} + +/* + * Add a bw_meter entry + */ +static int +add_bw_upcall(struct bw_upcall *req) +{ + struct mfc *mfc; + struct timeval delta = { BW_UPCALL_THRESHOLD_INTERVAL_MIN_SEC, + BW_UPCALL_THRESHOLD_INTERVAL_MIN_USEC }; + struct timeval now; + struct bw_meter *x; + uint32_t flags; + int s; + + if (!(mrt_api_config & MRT_MFC_BW_UPCALL)) + return EOPNOTSUPP; + + /* Test if the flags are valid */ + if (!(req->bu_flags & (BW_UPCALL_UNIT_PACKETS | BW_UPCALL_UNIT_BYTES))) + return EINVAL; + if (!(req->bu_flags & (BW_UPCALL_GEQ | BW_UPCALL_LEQ))) + return EINVAL; + if ((req->bu_flags & (BW_UPCALL_GEQ | BW_UPCALL_LEQ)) + == (BW_UPCALL_GEQ | BW_UPCALL_LEQ)) + return EINVAL; + + /* Test if the threshold time interval is valid */ + if (BW_TIMEVALCMP(&req->bu_threshold.b_time, &delta, <)) + return EINVAL; + + flags = compute_bw_meter_flags(req); + + /* + * Find if we have already same bw_meter entry + */ + s = splnet(); + mfc = mfc_find(req->bu_src.s_addr, req->bu_dst.s_addr); + if (mfc == NULL) { + splx(s); + return EADDRNOTAVAIL; + } + for (x = mfc->mfc_bw_meter; x != NULL; x = x->bm_mfc_next) { + if ((BW_TIMEVALCMP(&x->bm_threshold.b_time, + &req->bu_threshold.b_time, ==)) && + (x->bm_threshold.b_packets == req->bu_threshold.b_packets) && + (x->bm_threshold.b_bytes == req->bu_threshold.b_bytes) && + (x->bm_flags & BW_METER_USER_FLAGS) == flags) { + splx(s); + return 0; /* XXX Already installed */ + } + } + splx(s); + + /* Allocate the new bw_meter entry */ + x = (struct bw_meter *)malloc(sizeof(*x), M_BWMETER, M_NOWAIT); + if (x == NULL) + return ENOBUFS; + + /* Set the new bw_meter entry */ + x->bm_threshold.b_time = req->bu_threshold.b_time; + GET_TIME(now); + x->bm_start_time = now; + x->bm_threshold.b_packets = req->bu_threshold.b_packets; + x->bm_threshold.b_bytes = req->bu_threshold.b_bytes; + x->bm_measured.b_packets = 0; + x->bm_measured.b_bytes = 0; + x->bm_flags = flags; + x->bm_time_next = NULL; + x->bm_time_hash = BW_METER_BUCKETS; + + /* Add the new bw_meter entry to the front of entries for this MFC */ + s = splnet(); + x->bm_mfc = mfc; + x->bm_mfc_next = mfc->mfc_bw_meter; + mfc->mfc_bw_meter = x; + schedule_bw_meter(x, &now); + splx(s); + + return 0; +} + +static void +free_bw_list(struct bw_meter *list) +{ + while (list != NULL) { + struct bw_meter *x = list; + + list = list->bm_mfc_next; + unschedule_bw_meter(x); + free(x, M_BWMETER); + } +} + +/* + * Delete one or multiple bw_meter entries + */ +static int +del_bw_upcall(struct bw_upcall *req) +{ + struct mfc *mfc; + struct bw_meter *x; + int s; + + if (!(mrt_api_config & MRT_MFC_BW_UPCALL)) + return EOPNOTSUPP; + + s = splnet(); + /* Find the corresponding MFC entry */ + mfc = mfc_find(req->bu_src.s_addr, req->bu_dst.s_addr); + if (mfc == NULL) { + splx(s); + return EADDRNOTAVAIL; + } else if (req->bu_flags & BW_UPCALL_DELETE_ALL) { + /* + * Delete all bw_meter entries for this mfc + */ + struct bw_meter *list; + + list = mfc->mfc_bw_meter; + mfc->mfc_bw_meter = NULL; + splx(s); + free_bw_list(list); + return 0; + } else { /* Delete a single bw_meter entry */ + struct bw_meter *prev; + uint32_t flags = 0; + + flags = compute_bw_meter_flags(req); + + /* Find the bw_meter entry to delete */ + for (prev = NULL, x = mfc->mfc_bw_meter; x != NULL; + x = x->bm_mfc_next) { + if ((BW_TIMEVALCMP(&x->bm_threshold.b_time, + &req->bu_threshold.b_time, ==)) && + (x->bm_threshold.b_packets == req->bu_threshold.b_packets) && + (x->bm_threshold.b_bytes == req->bu_threshold.b_bytes) && + (x->bm_flags & BW_METER_USER_FLAGS) == flags) + break; + } + if (x != NULL) { /* Delete entry from the list for this MFC */ + if (prev != NULL) + prev->bm_mfc_next = x->bm_mfc_next; /* remove from middle*/ + else + x->bm_mfc->mfc_bw_meter = x->bm_mfc_next;/* new head of list */ + splx(s); + + unschedule_bw_meter(x); + /* Free the bw_meter entry */ + free(x, M_BWMETER); + return 0; + } else { + splx(s); + return EINVAL; + } + } + /* NOTREACHED */ +} + +/* + * Perform bandwidth measurement processing that may result in an upcall + */ +static void +bw_meter_receive_packet(struct bw_meter *x, int plen, struct timeval *nowp) +{ + struct timeval delta; + int s; + + s = splnet(); + delta = *nowp; + BW_TIMEVALDECR(&delta, &x->bm_start_time); + + if (x->bm_flags & BW_METER_GEQ) { + /* + * Processing for ">=" type of bw_meter entry + */ + if (BW_TIMEVALCMP(&delta, &x->bm_threshold.b_time, >)) { + /* Reset the bw_meter entry */ + x->bm_start_time = *nowp; + x->bm_measured.b_packets = 0; + x->bm_measured.b_bytes = 0; + x->bm_flags &= ~BW_METER_UPCALL_DELIVERED; + } + + /* Record that a packet is received */ + x->bm_measured.b_packets++; + x->bm_measured.b_bytes += plen; + + /* + * Test if we should deliver an upcall + */ + if (!(x->bm_flags & BW_METER_UPCALL_DELIVERED)) { + if (((x->bm_flags & BW_METER_UNIT_PACKETS) && + (x->bm_measured.b_packets >= x->bm_threshold.b_packets)) || + ((x->bm_flags & BW_METER_UNIT_BYTES) && + (x->bm_measured.b_bytes >= x->bm_threshold.b_bytes))) { + /* Prepare an upcall for delivery */ + bw_meter_prepare_upcall(x, nowp); + x->bm_flags |= BW_METER_UPCALL_DELIVERED; + } + } + } else if (x->bm_flags & BW_METER_LEQ) { + /* + * Processing for "<=" type of bw_meter entry + */ + if (BW_TIMEVALCMP(&delta, &x->bm_threshold.b_time, >)) { + /* + * We are behind time with the multicast forwarding table + * scanning for "<=" type of bw_meter entries, so test now + * if we should deliver an upcall. + */ + if (((x->bm_flags & BW_METER_UNIT_PACKETS) && + (x->bm_measured.b_packets <= x->bm_threshold.b_packets)) || + ((x->bm_flags & BW_METER_UNIT_BYTES) && + (x->bm_measured.b_bytes <= x->bm_threshold.b_bytes))) { + /* Prepare an upcall for delivery */ + bw_meter_prepare_upcall(x, nowp); + } + /* Reschedule the bw_meter entry */ + unschedule_bw_meter(x); + schedule_bw_meter(x, nowp); + } + + /* Record that a packet is received */ + x->bm_measured.b_packets++; + x->bm_measured.b_bytes += plen; + + /* + * Test if we should restart the measuring interval + */ + if ((x->bm_flags & BW_METER_UNIT_PACKETS && + x->bm_measured.b_packets <= x->bm_threshold.b_packets) || + (x->bm_flags & BW_METER_UNIT_BYTES && + x->bm_measured.b_bytes <= x->bm_threshold.b_bytes)) { + /* Don't restart the measuring interval */ + } else { + /* Do restart the measuring interval */ + /* + * XXX: note that we don't unschedule and schedule, because this + * might be too much overhead per packet. Instead, when we process + * all entries for a given timer hash bin, we check whether it is + * really a timeout. If not, we reschedule at that time. + */ + x->bm_start_time = *nowp; + x->bm_measured.b_packets = 0; + x->bm_measured.b_bytes = 0; + x->bm_flags &= ~BW_METER_UPCALL_DELIVERED; + } + } + splx(s); +} + +/* + * Prepare a bandwidth-related upcall + */ +static void +bw_meter_prepare_upcall(struct bw_meter *x, struct timeval *nowp) +{ + struct timeval delta; + struct bw_upcall *u; + int s; + + s = splnet(); + + /* + * Compute the measured time interval + */ + delta = *nowp; + BW_TIMEVALDECR(&delta, &x->bm_start_time); + + /* + * If there are too many pending upcalls, deliver them now + */ + if (bw_upcalls_n >= BW_UPCALLS_MAX) + bw_upcalls_send(); + + /* + * Set the bw_upcall entry + */ + u = &bw_upcalls[bw_upcalls_n++]; + u->bu_src = x->bm_mfc->mfc_origin; + u->bu_dst = x->bm_mfc->mfc_mcastgrp; + u->bu_threshold.b_time = x->bm_threshold.b_time; + u->bu_threshold.b_packets = x->bm_threshold.b_packets; + u->bu_threshold.b_bytes = x->bm_threshold.b_bytes; + u->bu_measured.b_time = delta; + u->bu_measured.b_packets = x->bm_measured.b_packets; + u->bu_measured.b_bytes = x->bm_measured.b_bytes; + u->bu_flags = 0; + if (x->bm_flags & BW_METER_UNIT_PACKETS) + u->bu_flags |= BW_UPCALL_UNIT_PACKETS; + if (x->bm_flags & BW_METER_UNIT_BYTES) + u->bu_flags |= BW_UPCALL_UNIT_BYTES; + if (x->bm_flags & BW_METER_GEQ) + u->bu_flags |= BW_UPCALL_GEQ; + if (x->bm_flags & BW_METER_LEQ) + u->bu_flags |= BW_UPCALL_LEQ; + + splx(s); +} + +/* + * Send the pending bandwidth-related upcalls + */ +static void +bw_upcalls_send(void) +{ + struct mbuf *m; + int len = bw_upcalls_n * sizeof(bw_upcalls[0]); + struct sockaddr_in k_igmpsrc = { sizeof k_igmpsrc, AF_INET }; + static struct igmpmsg igmpmsg = { 0, /* unused1 */ + 0, /* unused2 */ + IGMPMSG_BW_UPCALL,/* im_msgtype */ + 0, /* im_mbz */ + 0, /* im_vif */ + 0, /* unused3 */ + { 0 }, /* im_src */ + { 0 } }; /* im_dst */ + + if (bw_upcalls_n == 0) + return; /* No pending upcalls */ + + bw_upcalls_n = 0; + + /* + * Allocate a new mbuf, initialize it with the header and + * the payload for the pending calls. + */ + MGETHDR(m, M_DONTWAIT, MT_HEADER); + if (m == NULL) { + log(LOG_WARNING, "bw_upcalls_send: cannot allocate mbuf\n"); + return; + } + + m->m_len = m->m_pkthdr.len = 0; + m_copyback(m, 0, sizeof(struct igmpmsg), (caddr_t)&igmpmsg); + m_copyback(m, sizeof(struct igmpmsg), len, (caddr_t)&bw_upcalls[0]); + + /* + * Send the upcalls + * XXX do we need to set the address in k_igmpsrc ? + */ + mrtstat.mrts_upcalls++; + if (socket_send(ip_mrouter, m, &k_igmpsrc) < 0) { + log(LOG_WARNING, "bw_upcalls_send: ip_mrouter socket queue full\n"); + ++mrtstat.mrts_upq_sockfull; + } +} + +/* + * Compute the timeout hash value for the bw_meter entries + */ +#define BW_METER_TIMEHASH(bw_meter, hash) \ + do { \ + struct timeval next_timeval = (bw_meter)->bm_start_time; \ + \ + BW_TIMEVALADD(&next_timeval, &(bw_meter)->bm_threshold.b_time); \ + (hash) = next_timeval.tv_sec; \ + if (next_timeval.tv_usec) \ + (hash)++; /* XXX: make sure we don't timeout early */ \ + (hash) %= BW_METER_BUCKETS; \ + } while (0) + +/* + * Schedule a timer to process periodically bw_meter entry of type "<=" + * by linking the entry in the proper hash bucket. + */ +static void +schedule_bw_meter(struct bw_meter *x, struct timeval *nowp) +{ + int time_hash, s; + + if (!(x->bm_flags & BW_METER_LEQ)) + return; /* XXX: we schedule timers only for "<=" entries */ + + /* + * Reset the bw_meter entry + */ + s = splnet(); + x->bm_start_time = *nowp; + x->bm_measured.b_packets = 0; + x->bm_measured.b_bytes = 0; + x->bm_flags &= ~BW_METER_UPCALL_DELIVERED; + splx(s); + + /* + * Compute the timeout hash value and insert the entry + */ + BW_METER_TIMEHASH(x, time_hash); + x->bm_time_next = bw_meter_timers[time_hash]; + bw_meter_timers[time_hash] = x; + x->bm_time_hash = time_hash; +} + +/* + * Unschedule the periodic timer that processes bw_meter entry of type "<=" + * by removing the entry from the proper hash bucket. + */ +static void +unschedule_bw_meter(struct bw_meter *x) +{ + int time_hash; + struct bw_meter *prev, *tmp; + + if (!(x->bm_flags & BW_METER_LEQ)) + return; /* XXX: we schedule timers only for "<=" entries */ + + /* + * Compute the timeout hash value and delete the entry + */ + time_hash = x->bm_time_hash; + if (time_hash >= BW_METER_BUCKETS) + return; /* Entry was not scheduled */ + + for (prev = NULL, tmp = bw_meter_timers[time_hash]; + tmp != NULL; prev = tmp, tmp = tmp->bm_time_next) + if (tmp == x) + break; + + if (tmp == NULL) + panic("unschedule_bw_meter: bw_meter entry not found"); + + if (prev != NULL) + prev->bm_time_next = x->bm_time_next; + else + bw_meter_timers[time_hash] = x->bm_time_next; + + x->bm_time_next = NULL; + x->bm_time_hash = BW_METER_BUCKETS; +} + + +/* + * Process all "<=" type of bw_meter that should be processed now, + * and for each entry prepare an upcall if necessary. Each processed + * entry is rescheduled again for the (periodic) processing. + * + * This is run periodically (once per second normally). On each round, + * all the potentially matching entries are in the hash slot that we are + * looking at. + */ +static void +bw_meter_process() +{ + static uint32_t last_tv_sec; /* last time we processed this */ + + uint32_t loops; + int i, s; + struct timeval now, process_endtime; + + GET_TIME(now); + if (last_tv_sec == now.tv_sec) + return; /* nothing to do */ + + s = splnet(); + loops = now.tv_sec - last_tv_sec; + last_tv_sec = now.tv_sec; + if (loops > BW_METER_BUCKETS) + loops = BW_METER_BUCKETS; + + /* + * Process all bins of bw_meter entries from the one after the last + * processed to the current one. On entry, i points to the last bucket + * visited, so we need to increment i at the beginning of the loop. + */ + for (i = (now.tv_sec - loops) % BW_METER_BUCKETS; loops > 0; loops--) { + struct bw_meter *x, *tmp_list; + + if (++i >= BW_METER_BUCKETS) + i = 0; + + /* Disconnect the list of bw_meter entries from the bin */ + tmp_list = bw_meter_timers[i]; + bw_meter_timers[i] = NULL; + + /* Process the list of bw_meter entries */ + while (tmp_list != NULL) { + x = tmp_list; + tmp_list = tmp_list->bm_time_next; + + /* Test if the time interval is over */ + process_endtime = x->bm_start_time; + BW_TIMEVALADD(&process_endtime, &x->bm_threshold.b_time); + if (BW_TIMEVALCMP(&process_endtime, &now, >)) { + /* Not yet: reschedule, but don't reset */ + int time_hash; + + BW_METER_TIMEHASH(x, time_hash); + if (time_hash == i && process_endtime.tv_sec == now.tv_sec) { + /* + * XXX: somehow the bin processing is a bit ahead of time. + * Put the entry in the next bin. + */ + if (++time_hash >= BW_METER_BUCKETS) + time_hash = 0; + } + x->bm_time_next = bw_meter_timers[time_hash]; + bw_meter_timers[time_hash] = x; + x->bm_time_hash = time_hash; + + continue; + } + + /* + * Test if we should deliver an upcall + */ + if (((x->bm_flags & BW_METER_UNIT_PACKETS) && + (x->bm_measured.b_packets <= x->bm_threshold.b_packets)) || + ((x->bm_flags & BW_METER_UNIT_BYTES) && + (x->bm_measured.b_bytes <= x->bm_threshold.b_bytes))) { + /* Prepare an upcall for delivery */ + bw_meter_prepare_upcall(x, &now); + } + + /* + * Reschedule for next processing + */ + schedule_bw_meter(x, &now); + } + } + splx(s); + + /* Send all upcalls that are pending delivery */ + bw_upcalls_send(); +} + +/* + * A periodic function for sending all upcalls that are pending delivery + */ +static void +expire_bw_upcalls_send(void *unused) +{ + bw_upcalls_send(); + + bw_upcalls_ch = timeout(expire_bw_upcalls_send, NULL, BW_UPCALLS_PERIOD); +} + +/* + * A periodic function for periodic scanning of the multicast forwarding + * table for processing all "<=" bw_meter entries. + */ +static void +expire_bw_meter_process(void *unused) +{ + if (mrt_api_config & MRT_MFC_BW_UPCALL) + bw_meter_process(); + + bw_meter_ch = timeout(expire_bw_meter_process, NULL, BW_METER_PERIOD); +} + +/* + * End of bandwidth monitoring code + */ + +#ifdef PIM +/* + * Send the packet up to the user daemon, or eventually do kernel encapsulation + * + */ +static int +pim_register_send(struct ip *ip, struct vif *vifp, + struct mbuf *m, struct mfc *rt) +{ + struct mbuf *mb_copy, *mm; + + if (mrtdebug & DEBUG_PIM) + log(LOG_DEBUG, "pim_register_send: "); + + mb_copy = pim_register_prepare(ip, m); + if (mb_copy == NULL) + return ENOBUFS; + + /* + * Send all the fragments. Note that the mbuf for each fragment + * is freed by the sending machinery. + */ + for (mm = mb_copy; mm; mm = mb_copy) { + mb_copy = mm->m_nextpkt; + mm->m_nextpkt = 0; + mm = m_pullup(mm, sizeof(struct ip)); + if (mm != NULL) { + ip = mtod(mm, struct ip *); + if ((mrt_api_config & MRT_MFC_RP) && + (rt->mfc_rp.s_addr != INADDR_ANY)) { + pim_register_send_rp(ip, vifp, mm, rt); + } else { + pim_register_send_upcall(ip, vifp, mm, rt); + } + } + } + + return 0; +} + +/* + * Return a copy of the data packet that is ready for PIM Register + * encapsulation. + * XXX: Note that in the returned copy the IP header is a valid one. + */ +static struct mbuf * +pim_register_prepare(struct ip *ip, struct mbuf *m) +{ + struct mbuf *mb_copy = NULL; + int mtu; + + /* Take care of delayed checksums */ + if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { + in_delayed_cksum(m); + m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; + } + + /* + * Copy the old packet & pullup its IP header into the + * new mbuf so we can modify it. + */ + mb_copy = m_copypacket(m, M_DONTWAIT); + if (mb_copy == NULL) + return NULL; + mb_copy = m_pullup(mb_copy, ip->ip_hl << 2); + if (mb_copy == NULL) + return NULL; + + /* take care of the TTL */ + ip = mtod(mb_copy, struct ip *); + --ip->ip_ttl; + + /* Compute the MTU after the PIM Register encapsulation */ + mtu = 0xffff - sizeof(pim_encap_iphdr) - sizeof(pim_encap_pimhdr); + + if (ip->ip_len <= mtu) { + /* Turn the IP header into a valid one */ + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); + ip->ip_sum = 0; + ip->ip_sum = in_cksum(mb_copy, ip->ip_hl << 2); + } else { + /* Fragment the packet */ + if (ip_fragment(ip, &mb_copy, mtu, 0, CSUM_DELAY_IP) != 0) { + m_freem(mb_copy); + return NULL; + } + } + return mb_copy; +} + +/* + * Send an upcall with the data packet to the user-level process. + */ +static int +pim_register_send_upcall(struct ip *ip, struct vif *vifp, + struct mbuf *mb_copy, struct mfc *rt) +{ + struct mbuf *mb_first; + int len = ntohs(ip->ip_len); + struct igmpmsg *im; + struct sockaddr_in k_igmpsrc = { sizeof k_igmpsrc, AF_INET }; + + /* + * Add a new mbuf with an upcall header + */ + MGETHDR(mb_first, M_DONTWAIT, MT_HEADER); + if (mb_first == NULL) { + m_freem(mb_copy); + return ENOBUFS; + } + mb_first->m_data += max_linkhdr; + mb_first->m_pkthdr.len = len + sizeof(struct igmpmsg); + mb_first->m_len = sizeof(struct igmpmsg); + mb_first->m_next = mb_copy; + + /* Send message to routing daemon */ + im = mtod(mb_first, struct igmpmsg *); + im->im_msgtype = IGMPMSG_WHOLEPKT; + im->im_mbz = 0; + im->im_vif = vifp - viftable; + im->im_src = ip->ip_src; + im->im_dst = ip->ip_dst; + + k_igmpsrc.sin_addr = ip->ip_src; + + mrtstat.mrts_upcalls++; + + if (socket_send(ip_mrouter, mb_first, &k_igmpsrc) < 0) { + if (mrtdebug & DEBUG_PIM) + log(LOG_WARNING, + "mcast: pim_register_send_upcall: ip_mrouter socket queue full"); + ++mrtstat.mrts_upq_sockfull; + return ENOBUFS; + } + + /* Keep statistics */ + pimstat.pims_snd_registers_msgs++; + pimstat.pims_snd_registers_bytes += len; + + return 0; +} + +/* + * Encapsulate the data packet in PIM Register message and send it to the RP. + */ +static int +pim_register_send_rp(struct ip *ip, struct vif *vifp, + struct mbuf *mb_copy, struct mfc *rt) +{ + struct mbuf *mb_first; + struct ip *ip_outer; + struct pim_encap_pimhdr *pimhdr; + int len = ntohs(ip->ip_len); + vifi_t vifi = rt->mfc_parent; + + if ((vifi >= numvifs) || (viftable[vifi].v_lcl_addr.s_addr == 0)) { + m_freem(mb_copy); + return EADDRNOTAVAIL; /* The iif vif is invalid */ + } + + /* + * Add a new mbuf with the encapsulating header + */ + MGETHDR(mb_first, M_DONTWAIT, MT_HEADER); + if (mb_first == NULL) { + m_freem(mb_copy); + return ENOBUFS; + } + mb_first->m_data += max_linkhdr; + mb_first->m_len = sizeof(pim_encap_iphdr) + sizeof(pim_encap_pimhdr); + mb_first->m_next = mb_copy; + + mb_first->m_pkthdr.len = len + mb_first->m_len; + + /* + * Fill in the encapsulating IP and PIM header + */ + ip_outer = mtod(mb_first, struct ip *); + *ip_outer = pim_encap_iphdr; +#ifdef RANDOM_IP_ID + ip_outer->ip_id = ip_randomid(); +#else + ip_outer->ip_id = htons(ip_id++); +#endif + ip_outer->ip_len = len + sizeof(pim_encap_iphdr) + sizeof(pim_encap_pimhdr); + ip_outer->ip_src = viftable[vifi].v_lcl_addr; + ip_outer->ip_dst = rt->mfc_rp; + /* + * Copy the inner header TOS to the outer header, and take care of the + * IP_DF bit. + */ + ip_outer->ip_tos = ip->ip_tos; + if (ntohs(ip->ip_off) & IP_DF) + ip_outer->ip_off |= IP_DF; + pimhdr = (struct pim_encap_pimhdr *)((caddr_t)ip_outer + + sizeof(pim_encap_iphdr)); + *pimhdr = pim_encap_pimhdr; + /* If the iif crosses a border, set the Border-bit */ + if (rt->mfc_flags[vifi] & MRT_MFC_FLAGS_BORDER_VIF & mrt_api_config) + pimhdr->flags |= htonl(PIM_BORDER_REGISTER); + + mb_first->m_data += sizeof(pim_encap_iphdr); + pimhdr->pim.pim_cksum = in_cksum(mb_first, sizeof(pim_encap_pimhdr)); + mb_first->m_data -= sizeof(pim_encap_iphdr); + + if (vifp->v_rate_limit == 0) + tbf_send_packet(vifp, mb_first); + else + tbf_control(vifp, mb_first, ip, ip_outer->ip_len); + + /* Keep statistics */ + pimstat.pims_snd_registers_msgs++; + pimstat.pims_snd_registers_bytes += len; + + return 0; +} + +/* + * PIM-SMv2 and PIM-DM messages processing. + * Receives and verifies the PIM control messages, and passes them + * up to the listening socket, using rip_input(). + * The only message with special processing is the PIM_REGISTER message + * (used by PIM-SM): the PIM header is stripped off, and the inner packet + * is passed to if_simloop(). + */ +void +pim_input(struct mbuf *m, int off, int proto) +{ + struct ip *ip = mtod(m, struct ip *); + struct pim *pim; + int minlen; + int datalen = ip->ip_len; + int ip_tos; + int iphlen = off; + + /* Keep statistics */ + pimstat.pims_rcv_total_msgs++; + pimstat.pims_rcv_total_bytes += datalen; + + /* + * Validate lengths + */ + if (datalen < PIM_MINLEN) { + pimstat.pims_rcv_tooshort++; + log(LOG_ERR, "pim_input: packet size too small %d from %lx\n", + datalen, (u_long)ip->ip_src.s_addr); + m_freem(m); + return; + } + + /* + * If the packet is at least as big as a REGISTER, go agead + * and grab the PIM REGISTER header size, to avoid another + * possible m_pullup() later. + * + * PIM_MINLEN == pimhdr + u_int32_t == 4 + 4 = 8 + * PIM_REG_MINLEN == pimhdr + reghdr + encap_iphdr == 4 + 4 + 20 = 28 + */ + minlen = iphlen + (datalen >= PIM_REG_MINLEN ? PIM_REG_MINLEN : PIM_MINLEN); + /* + * Get the IP and PIM headers in contiguous memory, and + * possibly the PIM REGISTER header. + */ + if ((m->m_flags & M_EXT || m->m_len < minlen) && + (m = m_pullup(m, minlen)) == 0) { + log(LOG_ERR, "pim_input: m_pullup failure\n"); + return; + } + /* m_pullup() may have given us a new mbuf so reset ip. */ + ip = mtod(m, struct ip *); + ip_tos = ip->ip_tos; + + /* adjust mbuf to point to the PIM header */ + m->m_data += iphlen; + m->m_len -= iphlen; + pim = mtod(m, struct pim *); + + /* + * Validate checksum. If PIM REGISTER, exclude the data packet. + * + * XXX: some older PIMv2 implementations don't make this distinction, + * so for compatibility reason perform the checksum over part of the + * message, and if error, then over the whole message. + */ + if (PIM_VT_T(pim->pim_vt) == PIM_REGISTER && in_cksum(m, PIM_MINLEN) == 0) { + /* do nothing, checksum okay */ + } else if (in_cksum(m, datalen)) { + pimstat.pims_rcv_badsum++; + if (mrtdebug & DEBUG_PIM) + log(LOG_DEBUG, "pim_input: invalid checksum"); + m_freem(m); + return; + } + + /* PIM version check */ + if (PIM_VT_V(pim->pim_vt) < PIM_VERSION) { + pimstat.pims_rcv_badversion++; + log(LOG_ERR, "pim_input: incorrect version %d, expecting %d\n", + PIM_VT_V(pim->pim_vt), PIM_VERSION); + m_freem(m); + return; + } + + /* restore mbuf back to the outer IP */ + m->m_data -= iphlen; + m->m_len += iphlen; + + if (PIM_VT_T(pim->pim_vt) == PIM_REGISTER) { + /* + * Since this is a REGISTER, we'll make a copy of the register + * headers ip + pim + u_int32 + encap_ip, to be passed up to the + * routing daemon. + */ + struct sockaddr_in dst = { sizeof(dst), AF_INET }; + struct mbuf *mcp; + struct ip *encap_ip; + u_int32_t *reghdr; + + if ((reg_vif_num >= numvifs) || (reg_vif_num == VIFI_INVALID)) { + if (mrtdebug & DEBUG_PIM) + log(LOG_DEBUG, + "pim_input: register vif not set: %d\n", reg_vif_num); + m_freem(m); + return; + } + + /* + * Validate length + */ + if (datalen < PIM_REG_MINLEN) { + pimstat.pims_rcv_tooshort++; + pimstat.pims_rcv_badregisters++; + log(LOG_ERR, + "pim_input: register packet size too small %d from %lx\n", + datalen, (u_long)ip->ip_src.s_addr); + m_freem(m); + return; + } + + reghdr = (u_int32_t *)(pim + 1); + encap_ip = (struct ip *)(reghdr + 1); + + if (mrtdebug & DEBUG_PIM) { + log(LOG_DEBUG, + "pim_input[register], encap_ip: %lx -> %lx, encap_ip len %d\n", + (u_long)ntohl(encap_ip->ip_src.s_addr), + (u_long)ntohl(encap_ip->ip_dst.s_addr), + ntohs(encap_ip->ip_len)); + } + + /* verify the version number of the inner packet */ + if (encap_ip->ip_v != IPVERSION) { + pimstat.pims_rcv_badregisters++; + if (mrtdebug & DEBUG_PIM) { + log(LOG_DEBUG, "pim_input: invalid IP version (%d) " + "of the inner packet\n", encap_ip->ip_v); + } + m_freem(m); + return; + } + + /* verify the inner packet is destined to a mcast group */ + if (!IN_MULTICAST(ntohl(encap_ip->ip_dst.s_addr))) { + pimstat.pims_rcv_badregisters++; + if (mrtdebug & DEBUG_PIM) + log(LOG_DEBUG, + "pim_input: inner packet of register is not " + "multicast %lx\n", + (u_long)ntohl(encap_ip->ip_dst.s_addr)); + m_freem(m); + return; + } + + /* + * Copy the TOS from the outer IP header to the inner IP header. + */ + if (encap_ip->ip_tos != ip_tos) { + /* Outer TOS -> inner TOS */ + encap_ip->ip_tos = ip_tos; + /* Recompute the inner header checksum. Sigh... */ + + /* adjust mbuf to point to the inner IP header */ + m->m_data += (iphlen + PIM_MINLEN); + m->m_len -= (iphlen + PIM_MINLEN); + + encap_ip->ip_sum = 0; + encap_ip->ip_sum = in_cksum(m, encap_ip->ip_hl << 2); + + /* restore mbuf to point back to the outer IP header */ + m->m_data -= (iphlen + PIM_MINLEN); + m->m_len += (iphlen + PIM_MINLEN); + } + + /* If a NULL_REGISTER, pass it to the daemon */ + if ((ntohl(*reghdr) & PIM_NULL_REGISTER)) + goto pim_input_to_daemon; + + /* + * Decapsulate the inner IP packet and loopback to forward it + * as a normal multicast packet. Also, make a copy of the + * outer_iphdr + pimhdr + reghdr + encap_iphdr + * to pass to the daemon later, so it can take the appropriate + * actions (e.g., send back PIM_REGISTER_STOP). + * XXX: here m->m_data points to the outer IP header. + */ + mcp = m_copy(m, 0, iphlen + PIM_REG_MINLEN); + if (mcp == NULL) { + log(LOG_ERR, + "pim_input: pim register: could not copy register head\n"); + m_freem(m); + return; + } + + /* Keep statistics */ + /* XXX: registers_bytes include only the encap. mcast pkt */ + pimstat.pims_rcv_registers_msgs++; + pimstat.pims_rcv_registers_bytes += ntohs(encap_ip->ip_len); + + /* + * forward the inner ip packet; point m_data at the inner ip. + */ + m_adj(m, iphlen + PIM_MINLEN); + + if (mrtdebug & DEBUG_PIM) { + log(LOG_DEBUG, + "pim_input: forwarding decapsulated register: " + "src %lx, dst %lx, vif %d\n", + (u_long)ntohl(encap_ip->ip_src.s_addr), + (u_long)ntohl(encap_ip->ip_dst.s_addr), + reg_vif_num); + } + if_simloop(viftable[reg_vif_num].v_ifp, m, dst.sin_family, 0); + + /* prepare the register head to send to the mrouting daemon */ + m = mcp; + } + +pim_input_to_daemon: + /* + * Pass the PIM message up to the daemon; if it is a Register message, + * pass the 'head' only up to the daemon. This includes the + * outer IP header, PIM header, PIM-Register header and the + * inner IP header. + * XXX: the outer IP header pkt size of a Register is not adjust to + * reflect the fact that the inner multicast data is truncated. + */ + rip_input(m, iphlen, proto); + + return; +} +#endif /* PIM */ + static int ip_mroute_modevent(module_t mod, int type, void *unused) { diff --git a/sys/net/ip_mroute/ip_mroute.h b/sys/net/ip_mroute/ip_mroute.h index 0e3a727323..ec0964e65e 100644 --- a/sys/net/ip_mroute/ip_mroute.h +++ b/sys/net/ip_mroute/ip_mroute.h @@ -35,8 +35,8 @@ * SUCH DAMAGE. * * @(#)ip_mroute.h 8.1 (Berkeley) 6/10/93 - * $FreeBSD: src/sys/netinet/ip_mroute.h,v 1.17.2.2 2002/11/20 23:20:55 luigi Exp $ - * $DragonFly: src/sys/net/ip_mroute/ip_mroute.h,v 1.2 2003/06/17 04:28:51 dillon Exp $ + * $FreeBSD: src/sys/netinet/ip_mroute.h,v 1.17.2.3 2003/08/24 08:24:38 hsu Exp $ + * $DragonFly: src/sys/net/ip_mroute/ip_mroute.h,v 1.3 2003/08/24 23:07:07 hsu Exp $ */ #ifndef _NETINET_IP_MROUTE_H_ @@ -49,8 +49,12 @@ * Modified by Steve Deering, Stanford, February 1989. * Modified by Ajit Thyagarajan, PARC, August 1993. * Modified by Ajit Thyagarajan, PARC, August 1994. + * Modified by Ahmed Helmy, SGI, June 1996. + * Modified by Pavlin Radoslavov, ICSI, October 2002. * * MROUTING Revision: 3.3.1.3 + * and PIM-SMv2 and PIM-DM support, advanced API support, + * bandwidth metering and signaling. */ @@ -64,7 +68,12 @@ #define MRT_ADD_MFC 104 /* insert forwarding cache entry */ #define MRT_DEL_MFC 105 /* delete forwarding cache entry */ #define MRT_VERSION 106 /* get kernel version number */ -#define MRT_ASSERT 107 /* enable PIM assert processing */ +#define MRT_ASSERT 107 /* enable assert processing */ +#define MRT_PIM MRT_ASSERT /* enable PIM processing */ +#define MRT_API_SUPPORT 109 /* supported MRT API */ +#define MRT_API_CONFIG 110 /* config MRT API */ +#define MRT_ADD_BW_UPCALL 111 /* create bandwidth monitor */ +#define MRT_DEL_BW_UPCALL 112 /* delete bandwidth monitor */ #define GET_TIME(t) microtime(&t) @@ -100,10 +109,11 @@ struct vifctl { #define VIFF_TUNNEL 0x1 /* vif represents a tunnel end-point */ #define VIFF_SRCRT 0x2 /* tunnel uses IP source routing */ +#define VIFF_REGISTER 0x4 /* used for PIM Register encap/decap */ /* * Argument structure for MRT_ADD_MFC and MRT_DEL_MFC - * (mfcc_tos to be added at a future point) + * XXX if you change this, make sure to change struct mfcctl2 as well. */ struct mfcctl { struct in_addr mfcc_origin; /* ip origin of mcasts */ @@ -112,6 +122,94 @@ struct mfcctl { u_char mfcc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ }; +/* + * The new argument structure for MRT_ADD_MFC and MRT_DEL_MFC overlays + * and extends the old struct mfcctl. + */ +struct mfcctl2 { + /* the mfcctl fields */ + struct in_addr mfcc_origin; /* ip origin of mcasts */ + struct in_addr mfcc_mcastgrp; /* multicast group associated*/ + vifi_t mfcc_parent; /* incoming vif */ + u_char mfcc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ + + /* extension fields */ + uint8_t mfcc_flags[MAXVIFS]; /* the MRT_MFC_FLAGS_* flags */ + struct in_addr mfcc_rp; /* the RP address */ +}; +/* + * The advanced-API flags. + * + * The MRT_MFC_FLAGS_XXX API flags are also used as flags + * for the mfcc_flags field. + */ +#define MRT_MFC_FLAGS_DISABLE_WRONGVIF (1 << 0) /* disable WRONGVIF signals */ +#define MRT_MFC_FLAGS_BORDER_VIF (1 << 1) /* border vif */ +#define MRT_MFC_RP (1 << 8) /* enable RP address */ +#define MRT_MFC_BW_UPCALL (1 << 9) /* enable bw upcalls */ +#define MRT_MFC_FLAGS_ALL (MRT_MFC_FLAGS_DISABLE_WRONGVIF | \ + MRT_MFC_FLAGS_BORDER_VIF) +#define MRT_API_FLAGS_ALL (MRT_MFC_FLAGS_ALL | \ + MRT_MFC_RP | \ + MRT_MFC_BW_UPCALL) + +/* + * Structure for installing or delivering an upcall if the + * measured bandwidth is above or below a threshold. + * + * User programs (e.g. daemons) may have a need to know when the + * bandwidth used by some data flow is above or below some threshold. + * This interface allows the userland to specify the threshold (in + * bytes and/or packets) and the measurement interval. Flows are + * all packet with the same source and destination IP address. + * At the moment the code is only used for multicast destinations + * but there is nothing that prevents its use for unicast. + * + * The measurement interval cannot be shorter than some Tmin (currently, 3s). + * The threshold is set in packets and/or bytes per_interval. + * + * Measurement works as follows: + * + * For >= measurements: + * The first packet marks the start of a measurement interval. + * During an interval we count packets and bytes, and when we + * pass the threshold we deliver an upcall and we are done. + * The first packet after the end of the interval resets the + * count and restarts the measurement. + * + * For <= measurement: + * We start a timer to fire at the end of the interval, and + * then for each incoming packet we count packets and bytes. + * When the timer fires, we compare the value with the threshold, + * schedule an upcall if we are below, and restart the measurement + * (reschedule timer and zero counters). + */ + +struct bw_data { + struct timeval b_time; + uint64_t b_packets; + uint64_t b_bytes; +}; + +struct bw_upcall { + struct in_addr bu_src; /* source address */ + struct in_addr bu_dst; /* destination address */ + uint32_t bu_flags; /* misc flags (see below) */ +#define BW_UPCALL_UNIT_PACKETS (1 << 0) /* threshold (in packets) */ +#define BW_UPCALL_UNIT_BYTES (1 << 1) /* threshold (in bytes) */ +#define BW_UPCALL_GEQ (1 << 2) /* upcall if bw >= threshold */ +#define BW_UPCALL_LEQ (1 << 3) /* upcall if bw <= threshold */ +#define BW_UPCALL_DELETE_ALL (1 << 4) /* delete all upcalls for s,d*/ + struct bw_data bu_threshold; /* the bw threshold */ + struct bw_data bu_measured; /* the measured bw */ +}; + +/* max. number of upcalls to deliver together */ +#define BW_UPCALLS_MAX 128 +/* min. threshold time interval for bandwidth measurement */ +#define BW_UPCALL_THRESHOLD_INTERVAL_MIN_SEC 3 +#define BW_UPCALL_THRESHOLD_INTERVAL_MIN_USEC 0 + /* * The kernel's multicast routing statistics. */ @@ -180,17 +278,20 @@ struct vif { * at a future point) */ struct mfc { - struct in_addr mfc_origin; /* IP origin of mcasts */ - struct in_addr mfc_mcastgrp; /* multicast group associated*/ - vifi_t mfc_parent; /* incoming vif */ - u_char mfc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ - u_long mfc_pkt_cnt; /* pkt count for src-grp */ - u_long mfc_byte_cnt; /* byte count for src-grp */ - u_long mfc_wrong_if; /* wrong if for src-grp */ - int mfc_expire; /* time to clean entry up */ - struct timeval mfc_last_assert; /* last time I sent an assert*/ - struct rtdetq *mfc_stall; /* q of packets awaiting mfc */ - struct mfc *mfc_next; /* next mfc entry */ + struct in_addr mfc_origin; /* IP origin of mcasts */ + struct in_addr mfc_mcastgrp; /* multicast group associated*/ + vifi_t mfc_parent; /* incoming vif */ + u_char mfc_ttls[MAXVIFS]; /* forwarding ttls on vifs */ + u_long mfc_pkt_cnt; /* pkt count for src-grp */ + u_long mfc_byte_cnt; /* byte count for src-grp */ + u_long mfc_wrong_if; /* wrong if for src-grp */ + int mfc_expire; /* time to clean entry up */ + struct timeval mfc_last_assert; /* last time I sent an assert*/ + struct rtdetq *mfc_stall; /* q of packets awaiting mfc */ + struct mfc *mfc_next; /* next mfc entry */ + uint8_t mfc_flags[MAXVIFS]; /* the MRT_MFC_FLAGS_* flags */ + struct in_addr mfc_rp; /* the RP address */ + struct bw_meter *mfc_bw_meter; /* list of bandwidth meters */ }; /* @@ -201,8 +302,10 @@ struct igmpmsg { u_long unused1; u_long unused2; u_char im_msgtype; /* what type of message */ -#define IGMPMSG_NOCACHE 1 -#define IGMPMSG_WRONGVIF 2 +#define IGMPMSG_NOCACHE 1 /* no MFC in the kernel */ +#define IGMPMSG_WRONGVIF 2 /* packet came from wrong interface */ +#define IGMPMSG_WHOLEPKT 3 /* PIM pkt for user level encap. */ +#define IGMPMSG_BW_UPCALL 4 /* BW monitoring upcall */ u_char im_mbz; /* must be zero */ u_char im_vif; /* vif rec'd on */ u_char unused3; @@ -247,6 +350,32 @@ struct tbf struct mbuf *tbf_t; /* tail-insertion pointer */ }; +/* + * Structure for measuring the bandwidth and sending an upcall if the + * measured bandwidth is above or below a threshold. + */ +struct bw_meter { + struct bw_meter *bm_mfc_next; /* next bw meter (same mfc) */ + struct bw_meter *bm_time_next; /* next bw meter (same time) */ + uint32_t bm_time_hash; /* the time hash value */ + struct mfc *bm_mfc; /* the corresponding mfc */ + uint32_t bm_flags; /* misc flags (see below) */ +#define BW_METER_UNIT_PACKETS (1 << 0) /* threshold (in packets) */ +#define BW_METER_UNIT_BYTES (1 << 1) /* threshold (in bytes) */ +#define BW_METER_GEQ (1 << 2) /* upcall if bw >= threshold */ +#define BW_METER_LEQ (1 << 3) /* upcall if bw <= threshold */ +#define BW_METER_USER_FLAGS (BW_METER_UNIT_PACKETS | \ + BW_METER_UNIT_BYTES | \ + BW_METER_GEQ | \ + BW_METER_LEQ) + +#define BW_METER_UPCALL_DELIVERED (1 << 24) /* upcall was delivered */ + + struct bw_data bm_threshold; /* the upcall threshold */ + struct bw_data bm_measured; /* the measured bw */ + struct timeval bm_start_time; /* abs. time */ +}; + #ifdef _KERNEL struct sockopt; diff --git a/sys/netgraph/ksocket/ng_ksocket.c b/sys/netgraph/ksocket/ng_ksocket.c index cd7becf5e6..9a160a1bf5 100644 --- a/sys/netgraph/ksocket/ng_ksocket.c +++ b/sys/netgraph/ksocket/ng_ksocket.c @@ -36,8 +36,8 @@ * * Author: Archie Cobbs * - * $FreeBSD: src/sys/netgraph/ng_ksocket.c,v 1.5.2.13 2003/04/28 20:41:09 archie Exp $ - * $DragonFly: src/sys/netgraph/ksocket/ng_ksocket.c,v 1.5 2003/08/07 21:54:32 dillon Exp $ + * $FreeBSD: src/sys/netgraph/ng_ksocket.c,v 1.5.2.14 2003/08/24 08:24:38 hsu Exp $ + * $DragonFly: src/sys/netgraph/ksocket/ng_ksocket.c,v 1.6 2003/08/24 23:07:07 hsu Exp $ * $Whistle: ng_ksocket.c,v 1.1 1999/11/16 20:04:40 archie Exp $ */ @@ -149,6 +149,7 @@ static const struct ng_ksocket_alias ng_ksocket_protos[] = { { "swipe", IPPROTO_SWIPE, PF_INET }, { "encap", IPPROTO_ENCAP, PF_INET }, { "divert", IPPROTO_DIVERT, PF_INET }, + { "pim", IPPROTO_PIM, PF_INET }, { "ddp", ATPROTO_DDP, PF_APPLETALK }, { "aarp", ATPROTO_AARP, PF_APPLETALK }, { NULL, -1 }, diff --git a/sys/netinet/in.h b/sys/netinet/in.h index bd5a48dcfe..8534d1d674 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -31,8 +31,8 @@ * SUCH DAMAGE. * * @(#)in.h 8.3 (Berkeley) 1/3/94 - * $FreeBSD: src/sys/netinet/in.h,v 1.48.2.9 2002/12/01 14:03:10 sobomax Exp $ - * $DragonFly: src/sys/netinet/in.h,v 1.4 2003/08/23 11:18:00 rob Exp $ + * $FreeBSD: src/sys/netinet/in.h,v 1.48.2.10 2003/08/24 08:24:38 hsu Exp $ + * $DragonFly: src/sys/netinet/in.h,v 1.5 2003/08/24 23:07:07 hsu Exp $ */ #ifndef _NETINET_IN_H_ @@ -418,6 +418,22 @@ struct ip_mreq { { 0, 0 }, \ { 0, 0 }, \ { "ipsec", CTLTYPE_NODE }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ + { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ + { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ + { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ + { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ + { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ + { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ + { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ + { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { "pim", CTLTYPE_NODE }, \ } /* diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c index c361870617..3376084819 100644 --- a/sys/netinet/in_proto.c +++ b/sys/netinet/in_proto.c @@ -31,12 +31,13 @@ * SUCH DAMAGE. * * @(#)in_proto.c 8.2 (Berkeley) 2/9/95 - * $FreeBSD: src/sys/netinet/in_proto.c,v 1.53.2.6 2003/01/24 05:11:34 sam Exp $ - * $DragonFly: src/sys/netinet/in_proto.c,v 1.4 2003/08/23 11:18:00 rob Exp $ + * $FreeBSD: src/sys/netinet/in_proto.c,v 1.53.2.7 2003/08/24 08:24:38 hsu Exp $ + * $DragonFly: src/sys/netinet/in_proto.c,v 1.5 2003/08/24 23:07:07 hsu Exp $ */ #include "opt_ipdivert.h" #include "opt_ipx.h" +#include "opt_mrouting.h" #include "opt_ipsec.h" #include "opt_inet6.h" @@ -57,6 +58,9 @@ #include #include #include +#ifdef PIM +#include +#endif #include #include #include @@ -230,6 +234,14 @@ struct ipprotosw inetsw[] = { 0, 0, 0, 0, &rip_usrreqs }, +#endif +#ifdef PIM +{ SOCK_RAW, &inetdomain, IPPROTO_PIM, PR_ATOMIC|PR_ADDR|PR_LASTHDR, + pim_input, 0, 0, rip_ctloutput, + 0, + 0, 0, 0, 0, + &rip_usrreqs +}, #endif /* raw wildcard */ { SOCK_RAW, &inetdomain, 0, PR_ATOMIC|PR_ADDR, @@ -275,4 +287,6 @@ SYSCTL_NODE(_net_inet, IPPROTO_RAW, raw, CTLFLAG_RW, 0, "RAW"); #ifdef IPDIVERT SYSCTL_NODE(_net_inet, IPPROTO_DIVERT, divert, CTLFLAG_RW, 0, "DIVERT"); #endif - +#ifdef PIM +SYSCTL_NODE(_net_inet, IPPROTO_PIM, pim, CTLFLAG_RW, 0, "PIM"); +#endif diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index 267511328f..95d6721af3 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -32,7 +32,7 @@ * * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 * $FreeBSD: src/sys/netinet/ip_output.c,v 1.99.2.37 2003/04/15 06:44:45 silby Exp $ - * $DragonFly: src/sys/netinet/ip_output.c,v 1.7 2003/08/07 21:54:32 dillon Exp $ + * $DragonFly: src/sys/netinet/ip_output.c,v 1.8 2003/08/24 23:07:07 hsu Exp $ */ #define _IP_VHL @@ -126,15 +126,10 @@ extern struct protosw inetsw[]; * The mbuf opt, if present, will not be freed. */ int -ip_output(m0, opt, ro, flags, imo, inp) - struct mbuf *m0; - struct mbuf *opt; - struct route *ro; - int flags; - struct ip_moptions *imo; - struct inpcb *inp; +ip_output(struct mbuf *m0, struct mbuf *opt, struct route *ro, + int flags, struct ip_moptions *imo, struct inpcb *inp) { - struct ip *ip, *mhip; + struct ip *ip; struct ifnet *ifp = NULL; /* keep compiler happy */ struct mbuf *m; int hlen = sizeof (struct ip); @@ -354,6 +349,18 @@ ip_output(m0, opt, ro, flags, imo, inp) ip->ip_src = IA_SIN(ia)->sin_addr; } + if (ip_mrouter && (flags & IP_FORWARDING) == 0) { + /* + * XXX + * delayed checksums are not currently + * compatible with IP multicast routing + */ + if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { + in_delayed_cksum(m); + m->m_pkthdr.csum_flags &= + ~CSUM_DELAY_DATA; + } + } IN_LOOKUP_MULTI(pkt_dst, ifp, inm); if (inm != NULL && (imo == NULL || imo->imo_multicast_loop)) { @@ -450,7 +457,7 @@ ip_output(m0, opt, ro, flags, imo, inp) goto bad; } /* don't allow broadcast messages to be fragmented */ - if ((u_short)ip->ip_len > ifp->if_mtu) { + if (ip->ip_len > ifp->if_mtu) { error = EMSGSIZE; goto bad; } @@ -977,8 +984,7 @@ pass: * If small enough for interface, or the interface will take * care of the fragmentation for us, can just send directly. */ - if ((u_short)ip->ip_len <= ifp->if_mtu || - ifp->if_hwassist & CSUM_FRAGMENT) { + if (ip->ip_len <= ifp->if_mtu || ifp->if_hwassist & CSUM_FRAGMENT) { ip->ip_len = htons(ip->ip_len); ip->ip_off = htons(ip->ip_off); ip->ip_sum = 0; @@ -1025,10 +1031,7 @@ pass: (struct sockaddr *)dst, ro->ro_rt); goto done; } - /* - * Too large for interface; fragment if possible. - * Must be able to put at least 8 bytes per fragment. - */ + if (ip->ip_off & IP_DF) { error = EMSGSIZE; /* @@ -1038,49 +1041,178 @@ pass: * them, there is no way for one to update all its * routes when the MTU is changed. */ - if ((ro->ro_rt->rt_flags & (RTF_UP | RTF_HOST)) - && !(ro->ro_rt->rt_rmx.rmx_locks & RTV_MTU) - && (ro->ro_rt->rt_rmx.rmx_mtu > ifp->if_mtu)) { + if ((ro->ro_rt->rt_flags & (RTF_UP | RTF_HOST)) && + !(ro->ro_rt->rt_rmx.rmx_locks & RTV_MTU) && + (ro->ro_rt->rt_rmx.rmx_mtu > ifp->if_mtu)) { ro->ro_rt->rt_rmx.rmx_mtu = ifp->if_mtu; } ipstat.ips_cantfrag++; goto bad; } - len = (ifp->if_mtu - hlen) &~ 7; - if (len < 8) { - error = EMSGSIZE; + + /* + * Too large for interface; fragment if possible. If successful, + * on return, m will point to a list of packets to be sent. + */ + error = ip_fragment(ip, &m, ifp->if_mtu, ifp->if_hwassist, sw_csum); + if (error) goto bad; + for (; m; m = m0) { + m0 = m->m_nextpkt; + m->m_nextpkt = 0; +#ifdef IPSEC + /* clean ipsec history once it goes out of the node */ + ipsec_delaux(m); +#endif + if (error == 0) { + /* Record statistics for this interface address. */ + if (ia != NULL) { + ia->ia_ifa.if_opackets++; + ia->ia_ifa.if_obytes += m->m_pkthdr.len; + } + + error = (*ifp->if_output)(ifp, m, + (struct sockaddr *)dst, ro->ro_rt); + } else + m_freem(m); } + if (error == 0) + ipstat.ips_fragmented++; + +done: +#ifdef IPSEC + if (ro == &iproute && ro->ro_rt) { + RTFREE(ro->ro_rt); + ro->ro_rt = NULL; + } + if (sp != NULL) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ip_output call free SP:%p\n", sp)); + key_freesp(sp); + } +#endif +#ifdef FAST_IPSEC + if (ro == &iproute && ro->ro_rt) { + RTFREE(ro->ro_rt); + ro->ro_rt = NULL; + } + if (sp != NULL) + KEY_FREESP(&sp); +#endif + return (error); +bad: + m_freem(m); + goto done; +} + +/* + * Create a chain of fragments which fit the given mtu. m_frag points to the + * mbuf to be fragmented; on return it points to the chain with the fragments. + * Return 0 if no error. If error, m_frag may contain a partially built + * chain of fragments that should be freed by the caller. + * + * if_hwassist_flags is the hw offload capabilities (see if_data.ifi_hwassist) + * sw_csum contains the delayed checksums flags (e.g., CSUM_DELAY_IP). + */ +int +ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu, + u_long if_hwassist_flags, int sw_csum) +{ + int error = 0; + int hlen = IP_VHL_HL(ip->ip_vhl) << 2; + int len = (mtu - hlen) & ~7; /* size of payload in each fragment */ + int off; + struct mbuf *m0 = *m_frag; /* the original packet */ + int firstlen; + struct mbuf **mnext; + int nfrags; + + if (ip->ip_off & IP_DF) { /* Fragmentation not allowed */ + ipstat.ips_cantfrag++; + return EMSGSIZE; + } + + /* + * Must be able to put at least 8 bytes per fragment. + */ + if (len < 8) + return EMSGSIZE; + /* - * if the interface will not calculate checksums on + * If the interface will not calculate checksums on * fragmented packets, then do it here. */ - if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA && - (ifp->if_hwassist & CSUM_IP_FRAGS) == 0) { - in_delayed_cksum(m); - m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; + if (m0->m_pkthdr.csum_flags & CSUM_DELAY_DATA && + (if_hwassist_flags & CSUM_IP_FRAGS) == 0) { + in_delayed_cksum(m0); + m0->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } - { - int mhlen, firstlen = len; - struct mbuf **mnext = &m->m_nextpkt; - int nfrags = 1; + if (len > PAGE_SIZE) { + /* + * Fragment large datagrams such that each segment + * contains a multiple of PAGE_SIZE amount of data, + * plus headers. This enables a receiver to perform + * page-flipping zero-copy optimizations. + * + * XXX When does this help given that sender and receiver + * could have different page sizes, and also mtu could + * be less than the receiver's page size ? + */ + int newlen; + struct mbuf *m; + + for (m = m0, off = 0; m && (off+m->m_len) <= mtu; m = m->m_next) + off += m->m_len; + + /* + * firstlen (off - hlen) must be aligned on an + * 8-byte boundary + */ + if (off < hlen) + goto smart_frag_failure; + off = ((off - hlen) & ~7) + hlen; + newlen = (~PAGE_MASK) & mtu; + if ((newlen + sizeof (struct ip)) > mtu) { + /* we failed, go back the default */ +smart_frag_failure: + newlen = len; + off = hlen + len; + } + len = newlen; + + } else { + off = hlen + len; + } + + firstlen = off - hlen; + mnext = &m0->m_nextpkt; /* pointer to next packet */ /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto chain. + * Here, m0 is the original packet, m is the fragment being created. + * The fragments are linked off the m_nextpkt of the original + * packet, which after processing serves as the first fragment. */ - m0 = m; - mhlen = sizeof (struct ip); - for (off = hlen + len; off < (u_short)ip->ip_len; off += len) { + for (nfrags = 1; off < ip->ip_len; off += len, nfrags++) { + struct ip *mhip; /* ip header on the fragment */ + struct mbuf *m; + int mhlen = sizeof (struct ip); + MGETHDR(m, M_DONTWAIT, MT_HEADER); if (m == 0) { error = ENOBUFS; ipstat.ips_odropped++; - goto sendorfree; + goto done; } m->m_flags |= (m0->m_flags & M_MCAST) | M_FRAG; + /* + * In the first mbuf, leave room for the link header, then + * copy the original IP header including options. The payload + * goes into an additional mbuf chain returned by m_copy(). + */ m->m_data += max_linkhdr; mhip = mtod(m, struct ip *); *mhip = *ip; @@ -1089,108 +1221,53 @@ pass: mhip->ip_vhl = IP_MAKE_VHL(IPVERSION, mhlen >> 2); } m->m_len = mhlen; + /* XXX do we need to add ip->ip_off below ? */ mhip->ip_off = ((off - hlen) >> 3) + ip->ip_off; - if (off + len >= (u_short)ip->ip_len) - len = (u_short)ip->ip_len - off; - else + if (off + len >= ip->ip_len) { /* last fragment */ + len = ip->ip_len - off; + m->m_flags |= M_LASTFRAG; + } else mhip->ip_off |= IP_MF; mhip->ip_len = htons((u_short)(len + mhlen)); m->m_next = m_copy(m0, off, len); - if (m->m_next == 0) { - (void) m_free(m); + if (m->m_next == 0) { /* copy failed */ + m_free(m); error = ENOBUFS; /* ??? */ ipstat.ips_odropped++; - goto sendorfree; + goto done; } m->m_pkthdr.len = mhlen + len; m->m_pkthdr.rcvif = (struct ifnet *)0; m->m_pkthdr.csum_flags = m0->m_pkthdr.csum_flags; mhip->ip_off = htons(mhip->ip_off); mhip->ip_sum = 0; - if (sw_csum & CSUM_DELAY_IP) { - if (mhip->ip_vhl == IP_VHL_BORING) { - mhip->ip_sum = in_cksum_hdr(mhip); - } else { - mhip->ip_sum = in_cksum(m, mhlen); - } - } + if (sw_csum & CSUM_DELAY_IP) + mhip->ip_sum = in_cksum(m, mhlen); *mnext = m; mnext = &m->m_nextpkt; - nfrags++; } ipstat.ips_ofragments += nfrags; - /* set first/last markers for fragment chain */ - m->m_flags |= M_LASTFRAG; + /* set first marker for fragment chain */ m0->m_flags |= M_FIRSTFRAG | M_FRAG; m0->m_pkthdr.csum_data = nfrags; /* * Update first fragment by trimming what's been copied out - * and updating header, then send each fragment (in order). + * and updating header. */ - m = m0; - m_adj(m, hlen + firstlen - (u_short)ip->ip_len); - m->m_pkthdr.len = hlen + firstlen; - ip->ip_len = htons((u_short)m->m_pkthdr.len); + m_adj(m0, hlen + firstlen - ip->ip_len); + m0->m_pkthdr.len = hlen + firstlen; + ip->ip_len = htons((u_short)m0->m_pkthdr.len); ip->ip_off |= IP_MF; ip->ip_off = htons(ip->ip_off); ip->ip_sum = 0; - if (sw_csum & CSUM_DELAY_IP) { - if (ip->ip_vhl == IP_VHL_BORING) { - ip->ip_sum = in_cksum_hdr(ip); - } else { - ip->ip_sum = in_cksum(m, hlen); - } - } -sendorfree: - for (m = m0; m; m = m0) { - m0 = m->m_nextpkt; - m->m_nextpkt = 0; -#ifdef IPSEC - /* clean ipsec history once it goes out of the node */ - ipsec_delaux(m); -#endif - if (error == 0) { - /* Record statistics for this interface address. */ - if (ia != NULL) { - ia->ia_ifa.if_opackets++; - ia->ia_ifa.if_obytes += m->m_pkthdr.len; - } - - error = (*ifp->if_output)(ifp, m, - (struct sockaddr *)dst, ro->ro_rt); - } else - m_freem(m); - } + if (sw_csum & CSUM_DELAY_IP) + ip->ip_sum = in_cksum(m0, hlen); - if (error == 0) - ipstat.ips_fragmented++; - } done: -#ifdef IPSEC - if (ro == &iproute && ro->ro_rt) { - RTFREE(ro->ro_rt); - ro->ro_rt = NULL; - } - if (sp != NULL) { - KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ip_output call free SP:%p\n", sp)); - key_freesp(sp); - } -#endif /* IPSEC */ -#ifdef FAST_IPSEC - if (ro == &iproute && ro->ro_rt) { - RTFREE(ro->ro_rt); - ro->ro_rt = NULL; - } - if (sp != NULL) - KEY_FREESP(&sp); -#endif /* FAST_IPSEC */ - return (error); -bad: - m_freem(m); - goto done; + *m_frag = m0; + return error; } void diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index 2d00093f4c..677e703fde 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -31,8 +31,8 @@ * SUCH DAMAGE. * * @(#)ip_var.h 8.2 (Berkeley) 1/9/95 - * $FreeBSD: src/sys/netinet/ip_var.h,v 1.50.2.12 2003/02/27 04:50:02 silby Exp $ - * $DragonFly: src/sys/netinet/ip_var.h,v 1.2 2003/06/17 04:28:51 dillon Exp $ + * $FreeBSD: src/sys/netinet/ip_var.h,v 1.50.2.13 2003/08/24 08:24:38 hsu Exp $ + * $DragonFly: src/sys/netinet/ip_var.h,v 1.3 2003/08/24 23:07:08 hsu Exp $ */ #ifndef _NETINET_IP_VAR_H_ @@ -160,6 +160,8 @@ extern struct pr_usrreqs rip_usrreqs; int ip_ctloutput(struct socket *, struct sockopt *sopt); void ip_drain(void); +int ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu, + u_long if_hwassist_flags, int sw_csum); void ip_freemoptions(struct ip_moptions *); void ip_init(void); extern int (*ip_mforward)(struct ip *, struct ifnet *, struct mbuf *, diff --git a/sys/netinet/pim.h b/sys/netinet/pim.h new file mode 100644 index 0000000000..7664e9128e --- /dev/null +++ b/sys/netinet/pim.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1996-2000 + * University of Southern California/Information Sciences Institute. + * All rights reserved. + * + * 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. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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. + * + * $FreeBSD: src/sys/netinet/pim.h,v 1.1.2.1 2003/08/24 17:04:44 hsu Exp $ + * $DragonFly: src/sys/netinet/pim.h,v 1.1 2003/08/24 23:07:08 hsu Exp $ + */ + +#ifndef _NETINET_PIM_H_ +#define _NETINET_PIM_H_ + +/* + * Protocol Independent Multicast (PIM) definitions. + * RFC 2362, June 1998. + * + * Written by Ahmed Helmy, USC/SGI, July 1996. + * Modified by George Edmond Eddy (Rusty), ISI, February 1998. + * Modified by Pavlin Radoslavov, USC/ISI, May 1998, October 2000. + */ + +#ifndef _PIM_VT +#ifndef BYTE_ORDER +# error BYTE_ORDER is not defined! +#endif +#if (BYTE_ORDER != BIG_ENDIAN) && (BYTE_ORDER != LITTLE_ENDIAN) +# error BYTE_ORDER must be defined to either BIG_ENDIAN or LITTLE_ENDIAN +#endif +#endif /* ! _PIM_VT */ + +/* + * PIM packet header + */ +struct pim { +#ifdef _PIM_VT + uint8_t pim_vt; /* PIM version and message type */ +#else /* ! _PIM_VT */ +#if BYTE_ORDER == BIG_ENDIAN + u_int pim_vers:4, /* PIM protocol version */ + pim_type:4; /* PIM message type */ +#endif +#if BYTE_ORDER == LITTLE_ENDIAN + u_int pim_type:4, /* PIM message type */ + pim_vers:4; /* PIM protocol version */ +#endif +#endif /* ! _PIM_VT */ + uint8_t pim_reserved; /* Reserved */ + uint16_t pim_cksum; /* IP-style checksum */ +}; +/* KAME-related name backward compatibility */ +#define pim_ver pim_vers +#define pim_rsv pim_reserved + +#ifdef _PIM_VT +#define PIM_MAKE_VT(v, t) (0xff & (((v) << 4) | (0x0f & (t)))) +#define PIM_VT_V(x) (((x) >> 4) & 0x0f) +#define PIM_VT_T(x) ((x) & 0x0f) +#endif /* _PIM_VT */ + +#define PIM_VERSION 2 +#define PIM_MINLEN 8 /* PIM message min. length */ +#define PIM_REG_MINLEN (PIM_MINLEN+20) /* PIM Register hdr + inner IPv4 hdr */ +#define PIM6_REG_MINLEN (PIM_MINLEN+40) /* PIM Register hdr + inner IPv6 hdr */ + +/* + * PIM message types + */ +#define PIM_HELLO 0x0 /* PIM-SM and PIM-DM */ +#define PIM_REGISTER 0x1 /* PIM-SM only */ +#define PIM_REGISTER_STOP 0x2 /* PIM-SM only */ +#define PIM_JOIN_PRUNE 0x3 /* PIM-SM and PIM-DM */ +#define PIM_BOOTSTRAP 0x4 /* PIM-SM only */ +#define PIM_ASSERT 0x5 /* PIM-SM and PIM-DM */ +#define PIM_GRAFT 0x6 /* PIM-DM only */ +#define PIM_GRAFT_ACK 0x7 /* PIM-DM only */ +#define PIM_CAND_RP_ADV 0x8 /* PIM-SM only */ +#define PIM_ALL_DF_ELECTION 0xa /* Bidir-PIM-SM only */ + +/* + * PIM-Register message flags + */ +#define PIM_BORDER_REGISTER 0x80000000U /* The Border bit (host-order) */ +#define PIM_NULL_REGISTER 0x40000000U /* The Null-Register bit (host-order)*/ + +/* + * All-PIM-Routers IPv4 and IPv6 multicast addresses + */ +#define INADDR_ALLPIM_ROUTERS_GROUP (uint32_t)0xe000000dU /* 224.0.0.13 */ +#define IN6ADDR_LINKLOCAL_ALLPIM_ROUTERS "ff02::d" +#define IN6ADDR_LINKLOCAL_ALLPIM_ROUTERS_INIT \ + {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d }}} + +#endif /* _NETINET_PIM_H_ */ diff --git a/sys/netinet/pim_var.h b/sys/netinet/pim_var.h new file mode 100644 index 0000000000..827af9f106 --- /dev/null +++ b/sys/netinet/pim_var.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1998-2000 + * University of Southern California/Information Sciences Institute. + * All rights reserved. + * + * 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. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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. + * + * $FreeBSD: src/sys/netinet/pim_var.h,v 1.1.2.1 2003/08/24 17:04:44 hsu Exp $ + * $DragonFly: src/sys/netinet/pim_var.h,v 1.1 2003/08/24 23:07:08 hsu Exp $ + */ + +#ifndef _NETINET_PIM_VAR_H_ +#define _NETINET_PIM_VAR_H_ + +/* + * Protocol Independent Multicast (PIM), + * kernel variables and implementation-specific definitions. + * + * Written by George Edmond Eddy (Rusty), ISI, February 1998. + * Modified by Pavlin Radoslavov, USC/ISI, May 1998, Aug 1999, October 2000. + * Modified by Hitoshi Asaeda, WIDE, August 1998. + */ + +/* + * PIM statistics kept in the kernel + */ +struct pimstat { + u_quad_t pims_rcv_total_msgs; /* total PIM messages received */ + u_quad_t pims_rcv_total_bytes; /* total PIM bytes received */ + u_quad_t pims_rcv_tooshort; /* rcvd with too few bytes */ + u_quad_t pims_rcv_badsum; /* rcvd with bad checksum */ + u_quad_t pims_rcv_badversion; /* rcvd bad PIM version */ + u_quad_t pims_rcv_registers_msgs; /* rcvd regs. msgs (data only) */ + u_quad_t pims_rcv_registers_bytes; /* rcvd regs. bytes (data only) */ + u_quad_t pims_rcv_registers_wrongiif; /* rcvd regs. on wrong iif */ + u_quad_t pims_rcv_badregisters; /* rcvd invalid registers */ + u_quad_t pims_snd_registers_msgs; /* sent regs. msgs (data only) */ + u_quad_t pims_snd_registers_bytes; /* sent regs. bytes (data only) */ +}; + +/* + * Names for PIM sysctl objects + */ +#define PIMCTL_STATS 1 /* statistics (read-only) */ +#define PIMCTL_MAXID 2 + +#define PIMCTL_NAMES { \ + { 0, 0 }, \ + { "stats", CTLTYPE_STRUCT }, \ +} + +#ifdef _KERNEL +extern struct pimstat pimstat; + +void pim_input(struct mbuf *, int, int); +SYSCTL_DECL(_net_inet_pim); +#endif + +#endif /* _NETINET_PIM_VAR_H_ */ diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index 54489a9b86..5c35c984ab 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -31,8 +31,8 @@ * SUCH DAMAGE. * * @(#)raw_ip.c 8.7 (Berkeley) 5/15/95 - * $FreeBSD: src/sys/netinet/raw_ip.c,v 1.64.2.15 2003/01/24 10:52:50 hsu Exp $ - * $DragonFly: src/sys/netinet/raw_ip.c,v 1.5 2003/08/07 21:17:33 dillon Exp $ + * $FreeBSD: src/sys/netinet/raw_ip.c,v 1.64.2.16 2003/08/24 08:24:38 hsu Exp $ + * $DragonFly: src/sys/netinet/raw_ip.c,v 1.6 2003/08/24 23:07:08 hsu Exp $ */ #include "opt_inet6.h" @@ -341,6 +341,10 @@ rip_ctloutput(struct socket *so, struct sockopt *sopt) case MRT_DEL_MFC: case MRT_VERSION: case MRT_ASSERT: + case MRT_API_SUPPORT: + case MRT_API_CONFIG: + case MRT_ADD_BW_UPCALL: + case MRT_DEL_BW_UPCALL: error = ip_mrouter_get ? ip_mrouter_get(so, sopt) : EOPNOTSUPP; break; @@ -406,6 +410,10 @@ rip_ctloutput(struct socket *so, struct sockopt *sopt) case MRT_DEL_MFC: case MRT_VERSION: case MRT_ASSERT: + case MRT_API_SUPPORT: + case MRT_API_CONFIG: + case MRT_ADD_BW_UPCALL: + case MRT_DEL_BW_UPCALL: error = ip_mrouter_set ? ip_mrouter_set(so, sopt) : EOPNOTSUPP; break; diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index 1b63ee3430..977af38e24 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -31,8 +31,8 @@ * SUCH DAMAGE. * * @(#)socketvar.h 8.3 (Berkeley) 2/19/95 - * $FreeBSD: src/sys/sys/socketvar.h,v 1.46.2.9 2002/08/14 22:23:10 dg Exp $ - * $DragonFly: src/sys/sys/socketvar.h,v 1.4 2003/08/20 07:31:21 rob Exp $ + * $FreeBSD: src/sys/sys/socketvar.h,v 1.46.2.10 2003/08/24 08:24:39 hsu Exp $ + * $DragonFly: src/sys/sys/socketvar.h,v 1.5 2003/08/24 23:07:08 hsu Exp $ */ #ifndef _SYS_SOCKETVAR_H_ @@ -378,7 +378,7 @@ int solisten (struct socket *so, int backlog, struct thread *td); struct socket *sonewconn (struct socket *head, int connstatus); int sooptcopyin (struct sockopt *sopt, void *buf, size_t len, size_t minlen); -int sooptcopyout (struct sockopt *sopt, void *buf, size_t len); +int sooptcopyout (struct sockopt *sopt, const void *buf, size_t len); /* XXX; prepare mbuf for (__FreeBSD__ < 3) routines. */ int soopt_getm (struct sockopt *sopt, struct mbuf **mp); -- 2.41.0