diff --git a/sys/netinet/in.h b/sys/netinet/in.h index a4e9f0141126..63bd1cedfe17 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -486,6 +486,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 47c8b2463486..8199df80115d 100644 --- a/sys/netinet/in_proto.c +++ b/sys/netinet/in_proto.c @@ -36,6 +36,7 @@ #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 @@ -216,6 +220,14 @@ struct protosw inetsw[] = { &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 /* PIM */ /* raw wildcard */ { SOCK_RAW, &inetdomain, 0, PR_ATOMIC|PR_ADDR, rip_input, 0, 0, rip_ctloutput, @@ -260,4 +272,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_mroute.c b/sys/netinet/ip_mroute.c index f0f2471370c7..50ffdcabf93e 100644 --- a/sys/netinet/ip_mroute.c +++ b/sys/netinet/ip_mroute.c @@ -7,8 +7,16 @@ * 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 + * and PIM-SMv2 and PIM-DM support, advanced API support, + * bandwidth metering and signaling + * * $FreeBSD$ */ @@ -16,6 +24,10 @@ #include "opt_mrouting.h" #include "opt_random_ip_id.h" +#ifdef PIM +#define _PIM_VT 1 +#endif + #include #include #include @@ -43,6 +55,10 @@ #include #include #include +#ifdef PIM +#include +#include +#endif #include #include @@ -57,6 +73,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) @@ -120,6 +139,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. */ @@ -147,8 +240,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 *); @@ -164,6 +258,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. */ @@ -173,6 +293,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 */ @@ -230,7 +361,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; @@ -264,7 +397,20 @@ 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); + bzero((caddr_t)&mfc, sizeof(mfc)); /* zero out all fields */ + /* + * 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) @@ -280,6 +426,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; @@ -305,6 +471,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; @@ -405,6 +579,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"); @@ -433,7 +614,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); @@ -450,6 +631,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. */ @@ -464,6 +650,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; } @@ -471,11 +658,17 @@ 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 + if (encap_cookie) { encap_detach(encap_cookie); encap_cookie = NULL; @@ -505,6 +698,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; +} + /* * Decide if a packet is from a tunnelled peer. * Return 0 if not, 64 if so. XXX yuck.. 64 ??? @@ -607,11 +836,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) { @@ -646,6 +887,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; @@ -715,7 +970,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) { @@ -733,6 +988,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)); @@ -754,20 +1014,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; @@ -786,7 +1054,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; @@ -883,6 +1151,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; @@ -896,7 +1165,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; @@ -904,6 +1173,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; @@ -926,10 +1196,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; } @@ -963,8 +1242,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; @@ -979,7 +1258,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 { /* @@ -996,7 +1275,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) { @@ -1064,7 +1343,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) { @@ -1136,10 +1415,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; @@ -1223,6 +1508,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 { @@ -1262,6 +1557,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; } @@ -1278,21 +1578,30 @@ 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; /* if not found: ignore the packet */ + 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); @@ -1351,9 +1660,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; } @@ -1389,7 +1715,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) @@ -1408,16 +1734,6 @@ 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. - */ - 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. Try to fill the new @@ -1432,7 +1748,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; } @@ -1888,6 +2204,1023 @@ X_rsvp_input(struct mbuf *m, int off) 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 + 1) % BW_METER_BUCKETS; loops > 0; loops--) { + struct bw_meter *x, *tmp_list; + + if (++i >= BW_METER_BUCKETS) + i = 0; + + tmp_list = bw_meter_timers[i]; + bw_meter_timers[i] = NULL; + + 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); + 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; + + /* + * 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. + */ + 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_fragment(ip, &mb_copy, mtu, 0, 0) != 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) +{ + 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); + + return; +} +#endif /* PIM */ + static int ip_mroute_modevent(module_t mod, int type, void *unused) { diff --git a/sys/netinet/ip_mroute.h b/sys/netinet/ip_mroute.h index 1045ebbfccd4..58b1b6137023 100644 --- a/sys/netinet/ip_mroute.h +++ b/sys/netinet/ip_mroute.h @@ -48,8 +48,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. */ @@ -63,7 +67,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) @@ -99,10 +108,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 */ @@ -111,6 +121,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. */ @@ -179,17 +277,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 */ }; /* @@ -200,8 +301,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; @@ -246,6 +349,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/netinet/ip_output.c b/sys/netinet/ip_output.c index 1c6d8942b48a..60d5b1ddaf8d 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -124,15 +124,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); @@ -478,7 +473,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; } @@ -1014,8 +1009,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; @@ -1057,10 +1051,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; /* @@ -1070,149 +1061,23 @@ 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; - } - - /* - * 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 (len > PAGE_SIZE) { - /* - * Fragement 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. - */ - - int newlen; - struct mbuf *mtmp; - - for (mtmp = m, off = 0; - mtmp && ((off + mtmp->m_len) <= ifp->if_mtu); - mtmp = mtmp->m_next) { - off += mtmp->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) & ifp->if_mtu; - if ((newlen + sizeof (struct ip)) > ifp->if_mtu) { - /* we failed, go back the default */ -smart_frag_failure: - newlen = len; - off = hlen + len; - } - -/* printf("ipfrag: len = %d, hlen = %d, mhlen = %d, newlen = %d, off = %d\n", - len, hlen, sizeof (struct ip), newlen, off);*/ - - len = newlen; - - } else { - off = hlen + len; - } - - - - { - int mhlen, firstlen = off - hlen; - struct mbuf **mnext = &m->m_nextpkt; - int nfrags = 1; - - /* - * Loop through length of segment after first fragment, - * make new header and copy data of each part and link onto chain. - */ - m0 = m; - mhlen = sizeof (struct ip); - for (; off < (u_short)ip->ip_len; off += len) { - MGETHDR(m, M_DONTWAIT, MT_HEADER); - if (m == 0) { - error = ENOBUFS; - ipstat.ips_odropped++; - goto sendorfree; - } - m->m_flags |= (m0->m_flags & M_MCAST) | M_FRAG; - m->m_data += max_linkhdr; - mhip = mtod(m, struct ip *); - *mhip = *ip; - if (hlen > sizeof (struct ip)) { - mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); - mhip->ip_v = IPVERSION; - mhip->ip_hl = mhlen >> 2; - } - m->m_len = mhlen; - 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 - 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); - error = ENOBUFS; /* ??? */ - ipstat.ips_odropped++; - goto sendorfree; - } - m->m_pkthdr.len = mhlen + len; - m->m_pkthdr.rcvif = (struct ifnet *)0; -#ifdef MAC - mac_create_fragment(m0, m); -#endif - 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) - 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; - 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). - */ - 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); - ip->ip_off |= IP_MF; - ip->ip_off = htons(ip->ip_off); - ip->ip_sum = 0; - if (sw_csum & CSUM_DELAY_IP) - ip->ip_sum = in_cksum(m, hlen); -sendorfree: - for (m = m0; m; m = m0) { + for (; m; m = m0) { m0 = m->m_nextpkt; m->m_nextpkt = 0; #ifdef IPSEC @@ -1234,7 +1099,7 @@ sendorfree: if (error == 0) ipstat.ips_fragmented++; - } + done: #ifdef IPSEC if (ro == &iproute && ro->ro_rt) { @@ -1246,7 +1111,7 @@ done: printf("DP ip_output call free SP:%p\n", sp)); key_freesp(sp); } -#endif /* IPSEC */ +#endif #ifdef FAST_IPSEC if (ro == &iproute && ro->ro_rt) { RTFREE(ro->ro_rt); @@ -1254,13 +1119,181 @@ done: } if (sp != NULL) KEY_FREESP(&sp); -#endif /* FAST_IPSEC */ +#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->ip_hl << 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 + * fragmented packets, then do it here. + */ + 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; + } + + 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. + */ + 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 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; + if (hlen > sizeof (struct ip)) { + mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); + mhip->ip_v = IPVERSION; + mhip->ip_hl = 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 >= 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) { /* copy failed */ + m_free(m); + error = ENOBUFS; /* ??? */ + ipstat.ips_odropped++; + goto done; + } + m->m_pkthdr.len = mhlen + len; + m->m_pkthdr.rcvif = (struct ifnet *)0; +#ifdef MAC + mac_create_fragment(m0, m); +#endif + 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) + mhip->ip_sum = in_cksum(m, mhlen); + *mnext = m; + mnext = &m->m_nextpkt; + } + ipstat.ips_ofragments += nfrags; + + /* 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. + */ + 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) + ip->ip_sum = in_cksum(m0, hlen); + +done: + *m_frag = m0; + return error; +} + void in_delayed_cksum(struct mbuf *m) { @@ -1307,7 +1340,7 @@ ip_insertoptions(m, opt, phlen) unsigned optlen; optlen = opt->m_len - sizeof(p->ipopt_dst); - if (optlen + (u_short)ip->ip_len > IP_MAXPACKET) { + if (optlen + ip->ip_len > IP_MAXPACKET) { *phlen = 0; return (m); /* XXX should fail */ } diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index c8df2e673e4a..5a9a1ca1fe78 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -164,6 +164,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/raw_ip.c b/sys/netinet/raw_ip.c index dd40eb092d47..05447b4712e9 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -383,6 +383,10 @@ rip_ctloutput(so, 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; @@ -448,6 +452,10 @@ rip_ctloutput(so, 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;