#include "opt_mrouting.h"
#ifdef PIM
#define _PIM_VT 1
#endif
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/in_cksum.h>
#include <machine/stdarg.h>
#include <net/if.h>
#include <net/netisr.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/igmp.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include "ip_mroute.h"
#include <netinet/ip_var.h>
#ifdef PIM
#include <netinet/pim.h>
#include <netinet/pim_var.h>
#endif
#ifdef ALTQ
#include <netinet/in_pcb.h>
#endif
#include <netinet/udp.h>
static u_int rsvpdebug;
static u_int mrtdebug;
#define DEBUG_MFC 0x02
#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)
static MALLOC_DEFINE(M_MRTABLE, "mroutetbl", "multicast routing tables");
static struct mrtstat mrtstat;
SYSCTL_STRUCT(_net_inet_ip, OID_AUTO, mrtstat, CTLFLAG_RW,
&mrtstat, mrtstat,
"Multicast Routing Statistics (struct mrtstat, netinet/ip_mroute.h)");
static struct mfc *mfctable[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];
struct lwkt_token mroute_token = LWKT_TOKEN_INITIALIZER(mroute_token);
static struct callout expire_upcalls_ch;
static struct callout tbf_reprocess_q_ch;
#define EXPIRE_TIMEOUT (hz / 4)
#define UPCALL_EXPIRE 6
static struct tbf tbftable[MAXVIFS];
#define TBF_REPROCESS (hz / 100)
static struct ifnet multicast_decap_if[MAXVIFS];
#define ENCAP_TTL 64
#define ENCAP_PROTO IPPROTO_IPIP
static struct ip multicast_encap_iphdr = {
#if BYTE_ORDER == LITTLE_ENDIAN
sizeof(struct ip) >> 2, IPVERSION,
#else
IPVERSION, sizeof(struct ip) >> 2,
#endif
0,
sizeof(struct ip),
0,
0,
ENCAP_TTL, ENCAP_PROTO,
0,
};
static MALLOC_DEFINE(M_BWMETER, "bwmeter", "multicast upcall bw meters");
#define BW_METER_BUCKETS 1024
static struct bw_meter *bw_meter_timers[BW_METER_BUCKETS];
static struct callout bw_meter_ch;
#define BW_METER_PERIOD (hz)
static struct bw_upcall bw_upcalls[BW_UPCALLS_MAX];
static u_int bw_upcalls_n;
static struct callout bw_upcalls_ch;
#define BW_UPCALLS_PERIOD (hz)
#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)");
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,
sizeof(struct ip),
0,
0,
ENCAP_TTL,
IPPROTO_PIM,
0,
};
static struct pim_encap_pimhdr pim_encap_pimhdr = {
{
PIM_MAKE_VT(PIM_VERSION, PIM_REGISTER),
0,
0,
},
0
};
static struct ifnet multicast_register_if;
static vifi_t reg_vif_num = VIFI_INVALID;
#endif
static vifi_t numvifs;
static int have_encap_tunnel;
static u_long last_encap_src;
static struct vif *last_encap_vif;
static u_long X_ip_mcast_src(int vifi);
static int X_ip_mforward(struct ip *ip, struct ifnet *ifp,
struct mbuf *m, struct ip_moptions *imo);
static int X_ip_mrouter_done(void);
static int X_ip_mrouter_get(struct socket *so, struct sockopt *m);
static int X_ip_mrouter_set(struct socket *so, struct sockopt *m);
static int X_legal_vif_num(int vif);
static int X_mrt_ioctl(u_long cmd, caddr_t data);
static int get_sg_cnt(struct sioc_sg_req *);
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 mfcctl2 *);
static int del_mfc(struct mfcctl2 *);
static int set_api_config(uint32_t *);
static int socket_send(struct socket *, struct mbuf *, struct sockaddr_in *);
static int set_assert(int);
static void expire_upcalls(void *);
static int ip_mdq(struct mbuf *, struct ifnet *, struct mfc *, vifi_t);
static void phyint_send(struct ip *, struct vif *, struct mbuf *);
static void encap_send(struct ip *, struct vif *, struct mbuf *);
static void tbf_control(struct vif *, struct mbuf *, struct ip *, u_long);
static void tbf_queue(struct vif *, struct mbuf *);
static void tbf_process_q(struct vif *);
static void tbf_reprocess_q(void *);
static int tbf_dq_sel(struct vif *, struct ip *);
static void tbf_send_packet(struct vif *, struct mbuf *);
static void tbf_update_tokens(struct vif *);
static int priority(struct vif *, struct ip *);
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
static int pim_assert;
#define ASSERT_MSG_TIME 3000000
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;
#define MFCHASH(a, g) MFCHASHMOD(((a) >> 20) ^ ((a) >> 10) ^ (a) ^ \
((g) >> 20) ^ ((g) >> 10) ^ (g))
static struct mfc *
mfc_find(in_addr_t o, in_addr_t g)
{
struct mfc *rt;
for (rt = mfctable[MFCHASH(o,g)]; rt; rt = rt->mfc_next)
if ((rt->mfc_origin.s_addr == o) &&
(rt->mfc_mcastgrp.s_addr == g) && (rt->mfc_stall == NULL))
break;
return rt;
}
#define TV_DELTA(a, b, delta) { \
int xxs; \
delta = (a).tv_usec - (b).tv_usec; \
if ((xxs = (a).tv_sec - (b).tv_sec)) { \
switch (xxs) { \
case 2: \
delta += 1000000; \
\
case 1: \
delta += 1000000; \
break; \
default: \
delta += (1000000 * xxs); \
} \
} \
}
#define TV_LT(a, b) (((a).tv_usec < (b).tv_usec && \
(a).tv_sec <= (b).tv_sec) || (a).tv_sec < (b).tv_sec)
static int
X_ip_mrouter_set(struct socket *so, struct sockopt *sopt)
{
int error, optval;
vifi_t vifi;
struct vifctl vifc;
struct mfcctl2 mfc;
struct bw_upcall bw_upcall;
uint32_t i;
if (so != ip_mrouter && sopt->sopt_name != MRT_INIT)
return EPERM;
error = 0;
switch (sopt->sopt_name) {
case MRT_INIT:
error = soopt_to_kbuf(sopt, &optval, sizeof optval, sizeof optval);
if (error)
break;
error = ip_mrouter_init(so, optval);
break;
case MRT_DONE:
error = ip_mrouter_done();
break;
case MRT_ADD_VIF:
error = soopt_to_kbuf(sopt, &vifc, sizeof vifc, sizeof vifc);
if (error)
break;
error = add_vif(&vifc);
break;
case MRT_DEL_VIF:
error = soopt_to_kbuf(sopt, &vifi, sizeof vifi, sizeof vifi);
if (error)
break;
error = del_vif(vifi);
break;
case MRT_ADD_MFC:
case MRT_DEL_MFC:
if (sopt->sopt_name == MRT_ADD_MFC &&
mrt_api_config & MRT_API_FLAGS_ALL) {
error = soopt_to_kbuf(sopt, &mfc, sizeof(struct mfcctl2),
sizeof(struct mfcctl2));
} else {
error = soopt_to_kbuf(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)
error = add_mfc(&mfc);
else
error = del_mfc(&mfc);
break;
case MRT_ASSERT:
error = soopt_to_kbuf(sopt, &optval, sizeof optval, sizeof optval);
if (error)
break;
set_assert(optval);
break;
case MRT_API_CONFIG:
error = soopt_to_kbuf(sopt, &i, sizeof i, sizeof i);
if (!error)
error = set_api_config(&i);
if (!error)
soopt_from_kbuf(sopt, &i, sizeof i);
break;
case MRT_ADD_BW_UPCALL:
case MRT_DEL_BW_UPCALL:
error = soopt_to_kbuf(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;
}
return error;
}
static int
X_ip_mrouter_get(struct socket *so, struct sockopt *sopt)
{
int error;
static int version = 0x0305;
error = 0;
switch (sopt->sopt_name) {
case MRT_VERSION:
soopt_from_kbuf(sopt, &version, sizeof version);
break;
case MRT_ASSERT:
soopt_from_kbuf(sopt, &pim_assert, sizeof pim_assert);
break;
case MRT_API_SUPPORT:
soopt_from_kbuf(sopt, &mrt_api_support, sizeof mrt_api_support);
break;
case MRT_API_CONFIG:
soopt_from_kbuf(sopt, &mrt_api_config, sizeof mrt_api_config);
break;
default:
error = EOPNOTSUPP;
break;
}
return error;
}
static int
X_mrt_ioctl(u_long cmd, caddr_t data)
{
int error = 0;
switch (cmd) {
case SIOCGETVIFCNT:
error = get_vif_cnt((struct sioc_vif_req *)data);
break;
case SIOCGETSGCNT:
error = get_sg_cnt((struct sioc_sg_req *)data);
break;
default:
error = EINVAL;
break;
}
return error;
}
static int
get_sg_cnt(struct sioc_sg_req *req)
{
struct mfc *rt;
lwkt_gettoken(&mroute_token);
rt = mfc_find(req->src.s_addr, req->grp.s_addr);
if (rt == NULL) {
req->pktcnt = req->bytecnt = req->wrong_if = 0xffffffff;
lwkt_reltoken(&mroute_token);
return EADDRNOTAVAIL;
}
req->pktcnt = rt->mfc_pkt_cnt;
req->bytecnt = rt->mfc_byte_cnt;
req->wrong_if = rt->mfc_wrong_if;
lwkt_reltoken(&mroute_token);
return 0;
}
static int
get_vif_cnt(struct sioc_vif_req *req)
{
vifi_t vifi = req->vifi;
if (vifi >= numvifs)
return EINVAL;
req->icount = viftable[vifi].v_pkt_in;
req->ocount = viftable[vifi].v_pkt_out;
req->ibytes = viftable[vifi].v_bytes_in;
req->obytes = viftable[vifi].v_bytes_out;
return 0;
}
static int
ip_mrouter_init(struct socket *so, int version)
{
if (mrtdebug)
log(LOG_DEBUG, "ip_mrouter_init: so_type = %d, pr_protocol = %d\n",
so->so_type, so->so_proto->pr_protocol);
if (so->so_type != SOCK_RAW || so->so_proto->pr_protocol != IPPROTO_IGMP)
return EOPNOTSUPP;
if (version != 1)
return ENOPROTOOPT;
if (ip_mrouter != NULL)
return EADDRINUSE;
ip_mrouter = so;
bzero((caddr_t)mfctable, sizeof(mfctable));
bzero((caddr_t)nexpire, sizeof(nexpire));
pim_assert = 0;
bw_upcalls_n = 0;
bzero((caddr_t)bw_meter_timers, sizeof(bw_meter_timers));
callout_init(&expire_upcalls_ch);
callout_init(&bw_upcalls_ch);
callout_init(&bw_meter_ch);
callout_init(&tbf_reprocess_q_ch);
callout_reset(&expire_upcalls_ch, EXPIRE_TIMEOUT, expire_upcalls, NULL);
callout_reset(&bw_upcalls_ch, BW_UPCALLS_PERIOD,
expire_bw_upcalls_send, NULL);
callout_reset(&bw_meter_ch, BW_METER_PERIOD, expire_bw_meter_process, NULL);
mrt_api_config = 0;
if (mrtdebug)
log(LOG_DEBUG, "ip_mrouter_init\n");
return 0;
}
static int
X_ip_mrouter_done(void)
{
vifi_t vifi;
int i;
struct ifnet *ifp;
struct ifreq ifr;
struct mfc *rt;
struct rtdetq *rte;
lwkt_gettoken(&mroute_token);
for (vifi = 0; vifi < numvifs; vifi++) {
if (viftable[vifi].v_lcl_addr.s_addr != 0 &&
!(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);
so->sin_family = AF_INET;
so->sin_addr.s_addr = INADDR_ANY;
ifp = viftable[vifi].v_ifp;
if_allmulti(ifp, 0);
}
}
bzero((caddr_t)tbftable, sizeof(tbftable));
bzero((caddr_t)viftable, sizeof(viftable));
numvifs = 0;
pim_assert = 0;
callout_stop(&expire_upcalls_ch);
mrt_api_config = 0;
bw_upcalls_n = 0;
callout_stop(&bw_upcalls_ch);
callout_stop(&bw_meter_ch);
callout_stop(&tbf_reprocess_q_ch);
for (i = 0; i < MFCTBLSIZ; i++) {
for (rt = mfctable[i]; rt != NULL; ) {
struct mfc *nr = rt->mfc_next;
for (rte = rt->mfc_stall; rte != NULL; ) {
struct rtdetq *n = rte->next;
m_freem(rte->m);
kfree(rte, M_MRTABLE);
rte = n;
}
free_bw_list(rt->mfc_bw_meter);
kfree(rt, M_MRTABLE);
rt = nr;
}
}
bzero((caddr_t)mfctable, sizeof(mfctable));
bzero(bw_meter_timers, sizeof(bw_meter_timers));
last_encap_src = INADDR_ANY;
last_encap_vif = NULL;
#ifdef PIM
reg_vif_num = VIFI_INVALID;
#endif
have_encap_tunnel = 0;
ip_mrouter = NULL;
lwkt_reltoken(&mroute_token);
if (mrtdebug)
log(LOG_DEBUG, "ip_mrouter_done\n");
return 0;
}
static int
set_assert(int i)
{
if ((i != 1) && (i != 0))
return EINVAL;
pim_assert = i;
return 0;
}
static int
set_api_config(uint32_t *apival)
{
int i;
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;
}
static int
add_vif(struct vifctl *vifcp)
{
struct vif *vifp = viftable + vifcp->vifc_vifi;
struct sockaddr_in sin = {sizeof sin, AF_INET};
struct ifaddr *ifa;
struct ifnet *ifp;
int error, i;
struct tbf *v_tbf = tbftable + vifcp->vifc_vifi;
if (vifcp->vifc_vifi >= MAXVIFS)
return EINVAL;
if (vifp->v_lcl_addr.s_addr != INADDR_ANY)
return EADDRINUSE;
if (vifcp->vifc_lcl_addr.s_addr == INADDR_ANY)
return EADDRNOTAVAIL;
#ifdef PIM
if (vifcp->vifc_flags & VIFF_REGISTER) {
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) {
if (have_encap_tunnel == 0) {
have_encap_tunnel = 1;
for (i = 0; i < MAXVIFS; i++) {
if_initname(&multicast_decap_if[i], "mdecap", i);
}
}
ifp = &multicast_decap_if[vifcp->vifc_vifi];
bzero(&vifp->v_route, sizeof(vifp->v_route));
} else {
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) {
if_initname(&multicast_register_if, "register_vif", 0);
multicast_register_if.if_flags = IFF_LOOPBACK;
bzero(&vifp->v_route, sizeof(vifp->v_route));
reg_vif_num = vifcp->vifc_vifi;
}
#endif
} else {
if ((ifp->if_flags & IFF_MULTICAST) == 0)
return EOPNOTSUPP;
lwkt_gettoken(&mroute_token);
error = if_allmulti(ifp, 1);
lwkt_reltoken(&mroute_token);
if (error)
return error;
}
lwkt_gettoken(&mroute_token);
vifp->v_tbf = v_tbf;
GET_TIME(vifp->v_tbf->tbf_last_pkt_t);
vifp->v_tbf->tbf_n_tok = 0;
vifp->v_tbf->tbf_q_len = 0;
vifp->v_tbf->tbf_max_q_len = MAXQSIZE;
vifp->v_tbf->tbf_q = vifp->v_tbf->tbf_t = NULL;
vifp->v_flags = vifcp->vifc_flags;
vifp->v_threshold = vifcp->vifc_threshold;
vifp->v_lcl_addr = vifcp->vifc_lcl_addr;
vifp->v_rmt_addr = vifcp->vifc_rmt_addr;
vifp->v_ifp = ifp;
vifp->v_rate_limit= vifcp->vifc_rate_limit * 1024 / 1000;
vifp->v_rsvp_on = 0;
vifp->v_rsvpd = NULL;
vifp->v_pkt_in = 0;
vifp->v_pkt_out = 0;
vifp->v_bytes_in = 0;
vifp->v_bytes_out = 0;
if (numvifs <= vifcp->vifc_vifi) numvifs = vifcp->vifc_vifi + 1;
lwkt_reltoken(&mroute_token);
if (mrtdebug)
log(LOG_DEBUG, "add_vif #%d, lcladdr %lx, %s %lx, thresh %x, rate %d\n",
vifcp->vifc_vifi,
(u_long)ntohl(vifcp->vifc_lcl_addr.s_addr),
(vifcp->vifc_flags & VIFF_TUNNEL) ? "rmtaddr" : "mask",
(u_long)ntohl(vifcp->vifc_rmt_addr.s_addr),
vifcp->vifc_threshold,
vifcp->vifc_rate_limit);
return 0;
}
static int
del_vif(vifi_t vifi)
{
struct vif *vifp;
if (vifi >= numvifs)
return EINVAL;
vifp = &viftable[vifi];
if (vifp->v_lcl_addr.s_addr == INADDR_ANY)
return EADDRNOTAVAIL;
lwkt_gettoken(&mroute_token);
if (!(vifp->v_flags & (VIFF_TUNNEL | VIFF_REGISTER)))
if_allmulti(vifp->v_ifp, 0);
if (vifp == last_encap_vif) {
last_encap_vif = NULL;
last_encap_src = INADDR_ANY;
}
while (vifp->v_tbf->tbf_q) {
struct mbuf *m = vifp->v_tbf->tbf_q;
vifp->v_tbf->tbf_q = m->m_nextpkt;
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));
if (mrtdebug)
log(LOG_DEBUG, "del_vif %d, numvifs %d\n", vifi, numvifs);
for (vifi = numvifs; vifi > 0; vifi--)
if (viftable[vifi-1].v_lcl_addr.s_addr != INADDR_ANY)
break;
numvifs = vifi;
lwkt_reltoken(&mroute_token);
return 0;
}
static void
update_mfc_params(struct mfc *rt, struct mfcctl2 *mfccp)
{
int i;
rt->mfc_parent = mfccp->mfcc_parent;
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;
}
if (mrt_api_config & MRT_MFC_RP)
rt->mfc_rp = mfccp->mfcc_rp;
else
rt->mfc_rp.s_addr = INADDR_ANY;
}
static void
init_mfc_params(struct mfc *rt, struct mfcctl2 *mfccp)
{
rt->mfc_origin = mfccp->mfcc_origin;
rt->mfc_mcastgrp = mfccp->mfcc_mcastgrp;
update_mfc_params(rt, mfccp);
rt->mfc_pkt_cnt = 0;
rt->mfc_byte_cnt = 0;
rt->mfc_wrong_if = 0;
rt->mfc_last_assert.tv_sec = rt->mfc_last_assert.tv_usec = 0;
}
static int
add_mfc(struct mfcctl2 *mfccp)
{
struct mfc *rt;
u_long hash;
struct rtdetq *rte;
u_short nstl;
rt = mfc_find(mfccp->mfcc_origin.s_addr, mfccp->mfcc_mcastgrp.s_addr);
if (rt) {
if (mrtdebug & DEBUG_MFC)
log(LOG_DEBUG,"add_mfc update o %lx g %lx p %x\n",
(u_long)ntohl(mfccp->mfcc_origin.s_addr),
(u_long)ntohl(mfccp->mfcc_mcastgrp.s_addr),
mfccp->mfcc_parent);
lwkt_gettoken(&mroute_token);
update_mfc_params(rt, mfccp);
lwkt_reltoken(&mroute_token);
return 0;
}
lwkt_gettoken(&mroute_token);
hash = MFCHASH(mfccp->mfcc_origin.s_addr, mfccp->mfcc_mcastgrp.s_addr);
for (rt = mfctable[hash], nstl = 0; rt; rt = rt->mfc_next) {
if ((rt->mfc_origin.s_addr == mfccp->mfcc_origin.s_addr) &&
(rt->mfc_mcastgrp.s_addr == mfccp->mfcc_mcastgrp.s_addr) &&
(rt->mfc_stall != NULL)) {
if (nstl++)
log(LOG_ERR, "add_mfc %s o %lx g %lx p %x dbx %p\n",
"multiple kernel entries",
(u_long)ntohl(mfccp->mfcc_origin.s_addr),
(u_long)ntohl(mfccp->mfcc_mcastgrp.s_addr),
mfccp->mfcc_parent, (void *)rt->mfc_stall);
if (mrtdebug & DEBUG_MFC)
log(LOG_DEBUG,"add_mfc o %lx g %lx p %x dbg %p\n",
(u_long)ntohl(mfccp->mfcc_origin.s_addr),
(u_long)ntohl(mfccp->mfcc_mcastgrp.s_addr),
mfccp->mfcc_parent, (void *)rt->mfc_stall);
init_mfc_params(rt, mfccp);
rt->mfc_expire = 0;
nexpire[hash]--;
for (rte = rt->mfc_stall; rte != NULL; ) {
struct rtdetq *n = rte->next;
ip_mdq(rte->m, rte->ifp, rt, -1);
m_freem(rte->m);
kfree(rte, M_MRTABLE);
rte = n;
}
rt->mfc_stall = NULL;
}
}
if (nstl == 0) {
if (mrtdebug & DEBUG_MFC)
log(LOG_DEBUG,"add_mfc no upcall h %lu o %lx g %lx p %x\n",
hash, (u_long)ntohl(mfccp->mfcc_origin.s_addr),
(u_long)ntohl(mfccp->mfcc_mcastgrp.s_addr),
mfccp->mfcc_parent);
for (rt = mfctable[hash]; rt != NULL; rt = rt->mfc_next) {
if ((rt->mfc_origin.s_addr == mfccp->mfcc_origin.s_addr) &&
(rt->mfc_mcastgrp.s_addr == mfccp->mfcc_mcastgrp.s_addr)) {
init_mfc_params(rt, mfccp);
if (rt->mfc_expire)
nexpire[hash]--;
rt->mfc_expire = 0;
break;
}
}
if (rt == NULL) {
rt = kmalloc(sizeof(*rt), M_MRTABLE, M_INTWAIT | M_NULLOK);
if (rt == NULL) {
lwkt_reltoken(&mroute_token);
return ENOBUFS;
}
init_mfc_params(rt, mfccp);
rt->mfc_expire = 0;
rt->mfc_stall = NULL;
rt->mfc_bw_meter = NULL;
rt->mfc_next = mfctable[hash];
mfctable[hash] = rt;
}
}
lwkt_reltoken(&mroute_token);
return 0;
}
static int
del_mfc(struct mfcctl2 *mfccp)
{
struct in_addr origin;
struct in_addr mcastgrp;
struct mfc *rt;
struct mfc **nptr;
u_long hash;
struct bw_meter *list;
origin = mfccp->mfcc_origin;
mcastgrp = mfccp->mfcc_mcastgrp;
if (mrtdebug & DEBUG_MFC)
log(LOG_DEBUG,"del_mfc orig %lx mcastgrp %lx\n",
(u_long)ntohl(origin.s_addr), (u_long)ntohl(mcastgrp.s_addr));
lwkt_gettoken(&mroute_token);
hash = MFCHASH(origin.s_addr, mcastgrp.s_addr);
for (nptr = &mfctable[hash]; (rt = *nptr) != NULL; nptr = &rt->mfc_next)
if (origin.s_addr == rt->mfc_origin.s_addr &&
mcastgrp.s_addr == rt->mfc_mcastgrp.s_addr &&
rt->mfc_stall == NULL)
break;
if (rt == NULL) {
lwkt_reltoken(&mroute_token);
return EADDRNOTAVAIL;
}
*nptr = rt->mfc_next;
list = rt->mfc_bw_meter;
rt->mfc_bw_meter = NULL;
lwkt_reltoken(&mroute_token);
kfree(rt, M_MRTABLE);
free_bw_list(list);
return 0;
}
static int
socket_send(struct socket *s, struct mbuf *mm, struct sockaddr_in *src)
{
if (s) {
if (ssb_appendaddr(&s->so_rcv, (struct sockaddr *)src, mm, NULL) != 0) {
sorwakeup(s);
return 0;
} else
soroverflow(s);
}
m_freem(mm);
return -1;
}
#define TUNNEL_LEN 12
static int
X_ip_mforward(struct ip *ip, struct ifnet *ifp, struct mbuf *m,
struct ip_moptions *imo)
{
struct mfc *rt;
vifi_t vifi;
if (mrtdebug & DEBUG_FORWARD)
log(LOG_DEBUG, "ip_mforward: src %lx, dst %lx, ifp %p\n",
(u_long)ntohl(ip->ip_src.s_addr), (u_long)ntohl(ip->ip_dst.s_addr),
(void *)ifp);
if (ip->ip_hl < (sizeof(struct ip) + TUNNEL_LEN) >> 2 ||
((u_char *)(ip + 1))[1] != IPOPT_LSRR ) {
} else {
static time_t last_log;
if (last_log != time_uptime) {
last_log = time_uptime;
log(LOG_ERR,
"ip_mforward: received source-routed packet from %lx\n",
(u_long)ntohl(ip->ip_src.s_addr));
}
return 1;
}
if (imo && ((vifi = imo->imo_multicast_vif) < numvifs)) {
if (ip->ip_ttl < 255)
ip->ip_ttl++;
if (rsvpdebug && ip->ip_p == IPPROTO_RSVP) {
struct vif *vifp = viftable + vifi;
kprintf("Sending IPPROTO_RSVP from %lx to %lx on vif %d (%s%s)\n",
(long)ntohl(ip->ip_src.s_addr), (long)ntohl(ip->ip_dst.s_addr),
vifi,
(vifp->v_flags & VIFF_TUNNEL) ? "tunnel on " : "",
vifp->v_ifp->if_xname);
}
return ip_mdq(m, ifp, NULL, vifi);
}
if (rsvpdebug && ip->ip_p == IPPROTO_RSVP) {
kprintf("Warning: IPPROTO_RSVP from %lx to %lx without vif option\n",
(long)ntohl(ip->ip_src.s_addr), (long)ntohl(ip->ip_dst.s_addr));
if (!imo)
kprintf("In fact, no options were specified at all\n");
}
if (ip->ip_ttl <= 1 || ntohl(ip->ip_dst.s_addr) <= INADDR_MAX_LOCAL_GROUP)
return 0;
lwkt_gettoken(&mroute_token);
++mrtstat.mrts_mfc_lookups;
rt = mfc_find(ip->ip_src.s_addr, ip->ip_dst.s_addr);
if (rt != NULL) {
int ipres = ip_mdq(m, ifp, rt, -1);
lwkt_reltoken(&mroute_token);
return ipres;
} else {
struct mbuf *mb0;
struct rtdetq *rte;
u_long hash;
int hlen = ip->ip_hl << 2;
++mrtstat.mrts_mfc_misses;
mrtstat.mrts_no_route++;
if (mrtdebug & (DEBUG_FORWARD | DEBUG_MFC))
log(LOG_DEBUG, "ip_mforward: no rte s %lx g %lx\n",
(u_long)ntohl(ip->ip_src.s_addr),
(u_long)ntohl(ip->ip_dst.s_addr));
rte = kmalloc((sizeof *rte), M_MRTABLE, M_INTWAIT | M_NULLOK);
if (rte == NULL) {
lwkt_reltoken(&mroute_token);
return ENOBUFS;
}
mb0 = m_copypacket(m, M_NOWAIT);
if (mb0 && (M_HASCL(mb0) || mb0->m_len < hlen))
mb0 = m_pullup(mb0, hlen);
if (mb0 == NULL) {
kfree(rte, M_MRTABLE);
lwkt_reltoken(&mroute_token);
return ENOBUFS;
}
hash = MFCHASH(ip->ip_src.s_addr, ip->ip_dst.s_addr);
for (rt = mfctable[hash]; rt; rt = rt->mfc_next) {
if ((ip->ip_src.s_addr == rt->mfc_origin.s_addr) &&
(ip->ip_dst.s_addr == rt->mfc_mcastgrp.s_addr) &&
(rt->mfc_stall != NULL))
break;
}
if (rt == NULL) {
int i;
struct igmpmsg *im;
struct sockaddr_in k_igmpsrc = { sizeof k_igmpsrc, AF_INET };
struct mbuf *mm;
for (vifi=0; vifi < numvifs && viftable[vifi].v_ifp != ifp; vifi++)
;
if (vifi >= numvifs)
goto non_fatal;
rt = kmalloc(sizeof(*rt), M_MRTABLE, M_INTWAIT | M_NULLOK);
if (rt == NULL)
goto fail;
mm = m_copym(mb0, 0, hlen, M_NOWAIT);
if (mm == NULL)
goto fail1;
im = mtod(mm, struct igmpmsg *);
im->im_msgtype = IGMPMSG_NOCACHE;
im->im_mbz = 0;
im->im_vif = vifi;
mrtstat.mrts_upcalls++;
k_igmpsrc.sin_addr = ip->ip_src;
if (socket_send(ip_mrouter, mm, &k_igmpsrc) < 0) {
log(LOG_WARNING, "ip_mforward: ip_mrouter socket queue full\n");
++mrtstat.mrts_upq_sockfull;
fail1:
kfree(rt, M_MRTABLE);
fail:
kfree(rte, M_MRTABLE);
m_freem(mb0);
lwkt_reltoken(&mroute_token);
return ENOBUFS;
}
rt->mfc_origin.s_addr = ip->ip_src.s_addr;
rt->mfc_mcastgrp.s_addr = ip->ip_dst.s_addr;
rt->mfc_expire = UPCALL_EXPIRE;
nexpire[hash]++;
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;
rt->mfc_bw_meter = NULL;
rt->mfc_next = mfctable[hash];
mfctable[hash] = rt;
rt->mfc_stall = rte;
} else {
int npkts = 0;
struct rtdetq **p;
for (p = &rt->mfc_stall; *p != NULL; p = &(*p)->next)
npkts++;
if (npkts > MAX_UPQ) {
mrtstat.mrts_upq_ovflw++;
non_fatal:
kfree(rte, M_MRTABLE);
m_freem(mb0);
lwkt_reltoken(&mroute_token);
return 0;
}
*p = rte;
}
rte->m = mb0;
rte->ifp = ifp;
rte->next = NULL;
lwkt_reltoken(&mroute_token);
return 0;
}
}
static void
expire_upcalls(void *unused)
{
struct rtdetq *rte;
struct mfc *mfc, **nptr;
int i;
lwkt_gettoken(&mroute_token);
for (i = 0; i < MFCTBLSIZ; i++) {
if (nexpire[i] == 0)
continue;
nptr = &mfctable[i];
for (mfc = *nptr; mfc != NULL; mfc = *nptr) {
if (mfc->mfc_stall != NULL && mfc->mfc_expire != 0 &&
--mfc->mfc_expire == 0) {
if (mrtdebug & DEBUG_EXPIRE)
log(LOG_DEBUG, "expire_upcalls: expiring (%lx %lx)\n",
(u_long)ntohl(mfc->mfc_origin.s_addr),
(u_long)ntohl(mfc->mfc_mcastgrp.s_addr));
for (rte = mfc->mfc_stall; rte; ) {
struct rtdetq *n = rte->next;
m_freem(rte->m);
kfree(rte, M_MRTABLE);
rte = n;
}
++mrtstat.mrts_cache_cleanups;
nexpire[i]--;
while (mfc->mfc_bw_meter != NULL) {
struct bw_meter *x = mfc->mfc_bw_meter;
mfc->mfc_bw_meter = x->bm_mfc_next;
kfree(x, M_BWMETER);
}
*nptr = mfc->mfc_next;
kfree(mfc, M_MRTABLE);
} else {
nptr = &mfc->mfc_next;
}
}
}
callout_reset(&expire_upcalls_ch, EXPIRE_TIMEOUT, expire_upcalls, NULL);
lwkt_reltoken(&mroute_token);
}
static int
ip_mdq(struct mbuf *m, struct ifnet *ifp, struct mfc *rt, vifi_t xmt_vif)
{
struct ip *ip = mtod(m, struct ip *);
vifi_t vifi;
int plen = ntohs(ip->ip_len);
#define MC_SEND(ip,vifp,m) { \
if ((vifp)->v_flags & VIFF_TUNNEL) \
encap_send((ip), (vifp), (m)); \
else \
phyint_send((ip), (vifp), (m)); \
}
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;
}
vifi = rt->mfc_parent;
if ((vifi >= numvifs) || (viftable[vifi].v_ifp != ifp)) {
if (mrtdebug & DEBUG_FORWARD)
log(LOG_DEBUG, "wrong if: ifp %p vifi %d vififp %p\n",
(void *)ifp, vifi, (void *)viftable[vifi].v_ifp);
++mrtstat.mrts_wrong_if;
++rt->mfc_wrong_if;
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
for (vifi=0; vifi < numvifs && viftable[vifi].v_ifp != ifp; vifi++)
;
if (vifi >= numvifs)
return 0;
if (rt->mfc_flags[vifi] & MRT_MFC_FLAGS_DISABLE_WRONGVIF)
return 0;
GET_TIME(now);
TV_DELTA(rt->mfc_last_assert, now, delta);
if (delta > ASSERT_MSG_TIME) {
struct sockaddr_in k_igmpsrc = { sizeof k_igmpsrc, AF_INET };
struct igmpmsg *im;
int hlen = ip->ip_hl << 2;
struct mbuf *mm = m_copym(m, 0, hlen, M_NOWAIT);
if (mm && (M_HASCL(mm) || mm->m_len < hlen))
mm = m_pullup(mm, hlen);
if (mm == NULL)
return ENOBUFS;
rt->mfc_last_assert = now;
im = mtod(mm, struct igmpmsg *);
im->im_msgtype = IGMPMSG_WRONGVIF;
im->im_mbz = 0;
im->im_vif = vifi;
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");
++mrtstat.mrts_upq_sockfull;
return ENOBUFS;
}
}
}
return 0;
}
if (ip->ip_src.s_addr == viftable[vifi].v_lcl_addr.s_addr) {
viftable[vifi].v_pkt_out++;
viftable[vifi].v_bytes_out += plen;
} else {
viftable[vifi].v_pkt_in++;
viftable[vifi].v_bytes_in += plen;
}
rt->mfc_pkt_cnt++;
rt->mfc_byte_cnt += plen;
for (vifi = 0; vifi < numvifs; vifi++)
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);
}
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;
}
static int
X_legal_vif_num(int vif)
{
return (vif >= 0 && vif < numvifs);
}
static u_long
X_ip_mcast_src(int vifi)
{
if (vifi >= 0 && vifi < numvifs)
return viftable[vifi].v_lcl_addr.s_addr;
else
return INADDR_ANY;
}
static void
phyint_send(struct ip *ip, struct vif *vifp, struct mbuf *m)
{
struct mbuf *mb_copy;
int hlen = ip->ip_hl << 2;
mb_copy = m_copypacket(m, M_NOWAIT);
if (mb_copy && (M_HASCL(mb_copy) || mb_copy->m_len < hlen))
mb_copy = m_pullup(mb_copy, hlen);
if (mb_copy == NULL)
return;
if (vifp->v_rate_limit == 0)
tbf_send_packet(vifp, mb_copy);
else
tbf_control(vifp, mb_copy,
mtod(mb_copy, struct ip *), ntohs(ip->ip_len));
}
static void
encap_send(struct ip *ip, struct vif *vifp, struct mbuf *m)
{
struct mbuf *mb_copy;
struct ip *ip_copy;
int i, len = ntohs(ip->ip_len);
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
in_delayed_cksum(m);
m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
}
MGETHDR(mb_copy, M_NOWAIT, MT_HEADER);
if (mb_copy == NULL)
return;
mb_copy->m_data += max_linkhdr;
mb_copy->m_len = sizeof(multicast_encap_iphdr);
if ((mb_copy->m_next = m_copypacket(m, M_NOWAIT)) == NULL) {
m_freem(mb_copy);
return;
}
i = MHLEN - M_LEADINGSPACE(mb_copy);
if (i > len)
i = len;
mb_copy = m_pullup(mb_copy, i);
if (mb_copy == NULL)
return;
mb_copy->m_pkthdr.len = len + sizeof(multicast_encap_iphdr);
ip_copy = mtod(mb_copy, struct ip *);
*ip_copy = multicast_encap_iphdr;
ip_copy->ip_id = ip_newid();
ip_copy->ip_len = htons(ntohs(ip_copy->ip_len) + len);
ip_copy->ip_src = vifp->v_lcl_addr;
ip_copy->ip_dst = vifp->v_rmt_addr;
ip = (struct ip *)((caddr_t)ip_copy + sizeof(multicast_encap_iphdr));
--ip->ip_ttl;
ip->ip_sum = 0;
mb_copy->m_data += sizeof(multicast_encap_iphdr);
ip->ip_sum = in_cksum(mb_copy, ip->ip_hl << 2);
mb_copy->m_data -= sizeof(multicast_encap_iphdr);
if (vifp->v_rate_limit == 0)
tbf_send_packet(vifp, mb_copy);
else
tbf_control(vifp, mb_copy, ip, ntohs(ip_copy->ip_len));
}
static int
X_ipip_input(struct mbuf **mp, int *offp, int proto)
{
struct mbuf *m = *mp;
struct ip *ip = mtod(m, struct ip *);
int hlen = ip->ip_hl << 2;
if (!have_encap_tunnel) {
rip_input(mp, offp, proto);
return(IPPROTO_DONE);
}
*mp = NULL;
if (!IN_MULTICAST(ntohl(((struct ip *)((char *)ip+hlen))->ip_dst.s_addr))) {
++mrtstat.mrts_bad_tunnel;
m_freem(m);
return(IPPROTO_DONE);
}
if (ip->ip_src.s_addr != last_encap_src) {
struct vif *vifp = viftable;
struct vif *vife = vifp + numvifs;
last_encap_src = ip->ip_src.s_addr;
last_encap_vif = NULL;
for ( ; vifp < vife; ++vifp)
if (vifp->v_rmt_addr.s_addr == ip->ip_src.s_addr) {
if ((vifp->v_flags & (VIFF_TUNNEL|VIFF_SRCRT))
== VIFF_TUNNEL)
last_encap_vif = vifp;
break;
}
}
if (last_encap_vif == NULL) {
last_encap_src = INADDR_ANY;
mrtstat.mrts_cant_tunnel++;
m_freem(m);
if (mrtdebug)
log(LOG_DEBUG, "ip_mforward: no tunnel with %lx\n",
(u_long)ntohl(ip->ip_src.s_addr));
return(IPPROTO_DONE);
}
if (hlen > sizeof(struct ip))
ip_stripoptions(m);
m->m_data += sizeof(struct ip);
m->m_len -= sizeof(struct ip);
m->m_pkthdr.len -= sizeof(struct ip);
m->m_pkthdr.rcvif = last_encap_vif->v_ifp;
netisr_queue(NETISR_IP, m);
return(IPPROTO_DONE);
}
static void
tbf_control(struct vif *vifp, struct mbuf *m, struct ip *ip, u_long p_len)
{
struct tbf *t = vifp->v_tbf;
if (p_len > MAX_BKT_SIZE) {
mrtstat.mrts_pkt2large++;
m_freem(m);
return;
}
tbf_update_tokens(vifp);
if (t->tbf_q_len == 0) {
if (p_len <= t->tbf_n_tok) {
t->tbf_n_tok -= p_len;
tbf_send_packet(vifp, m);
} else {
tbf_queue(vifp, m);
callout_reset(&tbf_reprocess_q_ch, TBF_REPROCESS,
tbf_reprocess_q, vifp);
}
} else if (t->tbf_q_len < t->tbf_max_q_len) {
tbf_queue(vifp, m);
tbf_process_q(vifp);
} else {
if (!tbf_dq_sel(vifp, ip)) {
mrtstat.mrts_q_overflow++;
m_freem(m);
} else {
tbf_queue(vifp, m);
tbf_process_q(vifp);
}
}
}
static void
tbf_queue(struct vif *vifp, struct mbuf *m)
{
struct tbf *t = vifp->v_tbf;
lwkt_gettoken(&mroute_token);
if (t->tbf_t == NULL)
t->tbf_q = m;
else
t->tbf_t->m_nextpkt = m;
t->tbf_t = m;
#ifdef DIAGNOSTIC
if (m->m_nextpkt)
panic("tbf_queue: m_nextpkt");
#endif
m->m_nextpkt = NULL;
t->tbf_q_len++;
lwkt_reltoken(&mroute_token);
}
static void
tbf_process_q(struct vif *vifp)
{
struct tbf *t = vifp->v_tbf;
lwkt_gettoken(&mroute_token);
while (t->tbf_q_len > 0) {
struct mbuf *m = t->tbf_q;
int len = ntohs(mtod(m, struct ip *)->ip_len);
if (len > t->tbf_n_tok)
break;
t->tbf_n_tok -= len;
t->tbf_q = m->m_nextpkt;
if (--t->tbf_q_len == 0)
t->tbf_t = NULL;
m->m_nextpkt = NULL;
tbf_send_packet(vifp, m);
}
lwkt_reltoken(&mroute_token);
}
static void
tbf_reprocess_q(void *xvifp)
{
struct vif *vifp = xvifp;
if (ip_mrouter == NULL)
return;
tbf_update_tokens(vifp);
tbf_process_q(vifp);
if (vifp->v_tbf->tbf_q_len)
callout_reset(&tbf_reprocess_q_ch, TBF_REPROCESS,
tbf_reprocess_q, vifp);
}
static int
tbf_dq_sel(struct vif *vifp, struct ip *ip)
{
u_int p;
struct mbuf *m, *last;
struct mbuf **np;
struct tbf *t = vifp->v_tbf;
lwkt_gettoken(&mroute_token);
p = priority(vifp, ip);
np = &t->tbf_q;
last = NULL;
while ((m = *np) != NULL) {
if (p > priority(vifp, mtod(m, struct ip *))) {
*np = m->m_nextpkt;
if (m == t->tbf_t)
t->tbf_t = last;
m_freem(m);
if (--t->tbf_q_len == 0)
t->tbf_t = NULL;
mrtstat.mrts_drop_sel++;
lwkt_reltoken(&mroute_token);
return 1;
}
np = &m->m_nextpkt;
last = m;
}
lwkt_reltoken(&mroute_token);
return 0;
}
static void
tbf_send_packet(struct vif *vifp, struct mbuf *m)
{
lwkt_gettoken(&mroute_token);
if (vifp->v_flags & VIFF_TUNNEL)
ip_output(m, NULL, &vifp->v_route, IP_FORWARDING, NULL, NULL);
else {
struct ip_moptions imo;
int error;
static struct route ro;
imo.imo_multicast_ifp = vifp->v_ifp;
imo.imo_multicast_ttl = mtod(m, struct ip *)->ip_ttl - 1;
imo.imo_multicast_loop = 1;
imo.imo_multicast_vif = -1;
error = ip_output(m, NULL, &ro, IP_FORWARDING, &imo, NULL);
if (mrtdebug & DEBUG_XMIT)
log(LOG_DEBUG, "phyint_send on vif %d err %d\n",
(int)(vifp - viftable), error);
}
lwkt_reltoken(&mroute_token);
}
static void
tbf_update_tokens(struct vif *vifp)
{
struct timeval tp;
u_long tm;
struct tbf *t = vifp->v_tbf;
lwkt_gettoken(&mroute_token);
GET_TIME(tp);
TV_DELTA(tp, t->tbf_last_pkt_t, tm);
t->tbf_n_tok += tm * vifp->v_rate_limit / 1024 / 8;
t->tbf_last_pkt_t = tp;
if (t->tbf_n_tok > MAX_BKT_SIZE)
t->tbf_n_tok = MAX_BKT_SIZE;
lwkt_reltoken(&mroute_token);
}
static int
priority(struct vif *vifp, struct ip *ip)
{
int prio = 50;
if (ip->ip_p == IPPROTO_UDP) {
struct udphdr *udp = (struct udphdr *)(((char *)ip) + (ip->ip_hl << 2));
switch (ntohs(udp->uh_dport) & 0xc000) {
case 0x4000:
prio = 70;
break;
case 0x8000:
prio = 60;
break;
case 0xc000:
prio = 55;
break;
}
}
return prio;
}
static int
X_ip_rsvp_vif(struct socket *so, struct sockopt *sopt)
{
int error, vifi;
if (so->so_type != SOCK_RAW || so->so_proto->pr_protocol != IPPROTO_RSVP)
return EOPNOTSUPP;
error = soopt_to_kbuf(sopt, &vifi, sizeof vifi, sizeof vifi);
if (error)
return error;
lwkt_gettoken(&mroute_token);
if (vifi < 0 || vifi >= numvifs) {
lwkt_reltoken(&mroute_token);
return EADDRNOTAVAIL;
}
if (sopt->sopt_name == IP_RSVP_VIF_ON) {
if (viftable[vifi].v_rsvpd != NULL) {
lwkt_reltoken(&mroute_token);
return EADDRINUSE;
}
viftable[vifi].v_rsvpd = so;
if (!viftable[vifi].v_rsvp_on) {
viftable[vifi].v_rsvp_on = 1;
rsvp_on++;
}
} else {
viftable[vifi].v_rsvpd = NULL;
if (viftable[vifi].v_rsvp_on) {
viftable[vifi].v_rsvp_on = 0;
rsvp_on--;
}
}
lwkt_reltoken(&mroute_token);
return 0;
}
static void
X_ip_rsvp_force_done(struct socket *so)
{
int vifi;
if (so->so_type != SOCK_RAW || so->so_proto->pr_protocol != IPPROTO_RSVP)
return;
lwkt_gettoken(&mroute_token);
for (vifi = 0; vifi < numvifs; vifi++) {
if (viftable[vifi].v_rsvpd == so) {
viftable[vifi].v_rsvpd = NULL;
if (viftable[vifi].v_rsvp_on) {
viftable[vifi].v_rsvp_on = 0;
rsvp_on--;
}
}
}
lwkt_reltoken(&mroute_token);
}
static int
X_rsvp_input(struct mbuf **mp, int *offp, int proto)
{
int vifi;
struct mbuf *m = *mp;
struct ip *ip = mtod(m, struct ip *);
struct sockaddr_in rsvp_src = { sizeof rsvp_src, AF_INET };
struct ifnet *ifp;
#ifdef ALTQ
struct inpcb *inp;
struct socket *so;
struct mbuf *opts;
#endif
*mp = NULL;
if (rsvpdebug)
kprintf("rsvp_input: rsvp_on %d\n",rsvp_on);
if (!rsvp_on) {
m_freem(m);
return(IPPROTO_DONE);
}
lwkt_gettoken(&mroute_token);
if (rsvpdebug)
kprintf("rsvp_input: check vifs\n");
#ifdef DIAGNOSTIC
if (!(m->m_flags & M_PKTHDR))
panic("rsvp_input no hdr");
#endif
ifp = m->m_pkthdr.rcvif;
for (vifi = 0; vifi < numvifs; vifi++)
if (viftable[vifi].v_ifp == ifp)
break;
#ifdef ALTQ
if (vifi == numvifs || (so = viftable[vifi].v_rsvpd) == NULL) {
#else
if (vifi == numvifs || viftable[vifi].v_rsvpd == NULL) {
#endif
if (ip_rsvpd != NULL) {
if (rsvpdebug)
kprintf("rsvp_input: Sending packet up old-style socket\n");
*mp = m;
rip_input(mp, offp, proto);
} else {
if (rsvpdebug && vifi == numvifs)
kprintf("rsvp_input: Can't find vif for packet.\n");
else if (rsvpdebug && viftable[vifi].v_rsvpd == NULL)
kprintf("rsvp_input: No socket defined for vif %d\n",vifi);
m_freem(m);
}
lwkt_reltoken(&mroute_token);
return(IPPROTO_DONE);
}
rsvp_src.sin_addr = ip->ip_src;
if (rsvpdebug && m)
kprintf("rsvp_input: m->m_len = %d, ssb_space() = %ld\n",
m->m_len,ssb_space(&(viftable[vifi].v_rsvpd->so_rcv)));
#ifdef ALTQ
opts = NULL;
inp = (struct inpcb *)so->so_pcb;
if (inp->inp_flags & INP_CONTROLOPTS ||
inp->inp_socket->so_options & SO_TIMESTAMP) {
ip_savecontrol(inp, &opts, ip, m);
}
if (ssb_appendaddr(&so->so_rcv,
(struct sockaddr *)&rsvp_src,m, opts) == 0) {
m_freem(m);
if (opts)
m_freem(opts);
soroverflow(so);
if (rsvpdebug)
kprintf("rsvp_input: Failed to append to socket\n");
}
else {
sorwakeup(so);
if (rsvpdebug)
kprintf("rsvp_input: send packet up\n");
}
#else
if (socket_send(viftable[vifi].v_rsvpd, m, &rsvp_src) < 0) {
if (rsvpdebug)
kprintf("rsvp_input: Failed to append to socket\n");
} else {
if (rsvpdebug)
kprintf("rsvp_input: send packet up\n");
}
#endif
lwkt_reltoken(&mroute_token);
return(IPPROTO_DONE);
}
#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;
}
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;
if (!(mrt_api_config & MRT_MFC_BW_UPCALL))
return EOPNOTSUPP;
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;
if (BW_TIMEVALCMP(&req->bu_threshold.b_time, &delta, <))
return EINVAL;
flags = compute_bw_meter_flags(req);
lwkt_gettoken(&mroute_token);
mfc = mfc_find(req->bu_src.s_addr, req->bu_dst.s_addr);
if (mfc == NULL) {
lwkt_reltoken(&mroute_token);
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) {
lwkt_reltoken(&mroute_token);
return 0;
}
}
lwkt_reltoken(&mroute_token);
x = kmalloc(sizeof(*x), M_BWMETER, M_INTWAIT);
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;
lwkt_gettoken(&mroute_token);
x->bm_mfc = mfc;
x->bm_mfc_next = mfc->mfc_bw_meter;
mfc->mfc_bw_meter = x;
schedule_bw_meter(x, &now);
lwkt_reltoken(&mroute_token);
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);
kfree(x, M_BWMETER);
}
}
static int
del_bw_upcall(struct bw_upcall *req)
{
struct mfc *mfc;
struct bw_meter *x;
if (!(mrt_api_config & MRT_MFC_BW_UPCALL))
return EOPNOTSUPP;
lwkt_gettoken(&mroute_token);
mfc = mfc_find(req->bu_src.s_addr, req->bu_dst.s_addr);
if (mfc == NULL) {
lwkt_reltoken(&mroute_token);
return EADDRNOTAVAIL;
} else if (req->bu_flags & BW_UPCALL_DELETE_ALL) {
struct bw_meter *list;
list = mfc->mfc_bw_meter;
mfc->mfc_bw_meter = NULL;
lwkt_reltoken(&mroute_token);
free_bw_list(list);
return 0;
} else {
struct bw_meter *prev;
uint32_t flags = 0;
flags = compute_bw_meter_flags(req);
for (prev = NULL, x = mfc->mfc_bw_meter; x != NULL;
prev = x, 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) {
if (prev != NULL)
prev->bm_mfc_next = x->bm_mfc_next;
else
x->bm_mfc->mfc_bw_meter = x->bm_mfc_next;
unschedule_bw_meter(x);
lwkt_reltoken(&mroute_token);
kfree(x, M_BWMETER);
return 0;
} else {
lwkt_reltoken(&mroute_token);
return EINVAL;
}
}
}
static void
bw_meter_receive_packet(struct bw_meter *x, int plen, struct timeval *nowp)
{
struct timeval delta;
lwkt_gettoken(&mroute_token);
delta = *nowp;
BW_TIMEVALDECR(&delta, &x->bm_start_time);
if (x->bm_flags & BW_METER_GEQ) {
if (BW_TIMEVALCMP(&delta, &x->bm_threshold.b_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;
}
x->bm_measured.b_packets++;
x->bm_measured.b_bytes += plen;
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))) {
bw_meter_prepare_upcall(x, nowp);
x->bm_flags |= BW_METER_UPCALL_DELIVERED;
}
}
} else if (x->bm_flags & BW_METER_LEQ) {
if (BW_TIMEVALCMP(&delta, &x->bm_threshold.b_time, >)) {
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))) {
bw_meter_prepare_upcall(x, nowp);
}
unschedule_bw_meter(x);
schedule_bw_meter(x, nowp);
}
x->bm_measured.b_packets++;
x->bm_measured.b_bytes += plen;
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)) {
} else {
x->bm_start_time = *nowp;
x->bm_measured.b_packets = 0;
x->bm_measured.b_bytes = 0;
x->bm_flags &= ~BW_METER_UPCALL_DELIVERED;
}
}
lwkt_reltoken(&mroute_token);
}
static void
bw_meter_prepare_upcall(struct bw_meter *x, struct timeval *nowp)
{
struct timeval delta;
struct bw_upcall *u;
lwkt_gettoken(&mroute_token);
delta = *nowp;
BW_TIMEVALDECR(&delta, &x->bm_start_time);
if (bw_upcalls_n >= BW_UPCALLS_MAX)
bw_upcalls_send();
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;
lwkt_reltoken(&mroute_token);
}
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,
0,
IGMPMSG_BW_UPCALL,
0,
0,
0,
{ 0 },
{ 0 } };
if (bw_upcalls_n == 0)
return;
bw_upcalls_n = 0;
MGETHDR(m, M_NOWAIT, MT_HEADER);
if (m == NULL) {
log(LOG_WARNING, "bw_upcalls_send: cannot allocate mbuf\n");
return;
}
m_copyback(m, 0, sizeof(struct igmpmsg), &igmpmsg);
m_copyback(m, sizeof(struct igmpmsg), len, &bw_upcalls[0]);
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;
}
}
#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)++; \
(hash) %= BW_METER_BUCKETS; \
} while (0)
static void
schedule_bw_meter(struct bw_meter *x, struct timeval *nowp)
{
int time_hash;
if (!(x->bm_flags & BW_METER_LEQ))
return;
lwkt_gettoken(&mroute_token);
x->bm_start_time = *nowp;
x->bm_measured.b_packets = 0;
x->bm_measured.b_bytes = 0;
x->bm_flags &= ~BW_METER_UPCALL_DELIVERED;
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;
lwkt_reltoken(&mroute_token);
}
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;
time_hash = x->bm_time_hash;
if (time_hash >= BW_METER_BUCKETS)
return;
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;
}
static void
bw_meter_process(void)
{
static uint32_t last_tv_sec;
uint32_t loops;
int i;
struct timeval now, process_endtime;
GET_TIME(now);
if (last_tv_sec == now.tv_sec)
return;
lwkt_gettoken(&mroute_token);
loops = now.tv_sec - last_tv_sec;
last_tv_sec = now.tv_sec;
if (loops > BW_METER_BUCKETS)
loops = BW_METER_BUCKETS;
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;
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;
process_endtime = x->bm_start_time;
BW_TIMEVALADD(&process_endtime, &x->bm_threshold.b_time);
if (BW_TIMEVALCMP(&process_endtime, &now, >)) {
int time_hash;
BW_METER_TIMEHASH(x, time_hash);
if (time_hash == i && process_endtime.tv_sec == now.tv_sec) {
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;
}
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))) {
bw_meter_prepare_upcall(x, &now);
}
schedule_bw_meter(x, &now);
}
}
bw_upcalls_send();
lwkt_reltoken(&mroute_token);
}
static void
expire_bw_upcalls_send(void *unused)
{
bw_upcalls_send();
callout_reset(&bw_upcalls_ch, BW_UPCALLS_PERIOD,
expire_bw_upcalls_send, NULL);
}
static void
expire_bw_meter_process(void *unused)
{
if (mrt_api_config & MRT_MFC_BW_UPCALL)
bw_meter_process();
callout_reset(&bw_meter_ch, BW_METER_PERIOD,
expire_bw_meter_process, NULL);
}
#ifdef PIM
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;
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;
}
static struct mbuf *
pim_register_prepare(struct ip *ip, struct mbuf *m)
{
struct mbuf *mb_copy = NULL;
int mtu;
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
in_delayed_cksum(m);
m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
}
mb_copy = m_copypacket(m, M_NOWAIT);
if (mb_copy == NULL)
return NULL;
mb_copy = m_pullup(mb_copy, ip->ip_hl << 2);
if (mb_copy == NULL)
return NULL;
ip = mtod(mb_copy, struct ip *);
--ip->ip_ttl;
mtu = 0xffff - sizeof(pim_encap_iphdr) - sizeof(pim_encap_pimhdr);
if (ntohs(ip->ip_len) <= mtu) {
ip->ip_sum = 0;
ip->ip_sum = in_cksum(mb_copy, ip->ip_hl << 2);
} else {
if (ip_fragment(ip, &mb_copy, mtu, 0, CSUM_DELAY_IP) != 0) {
m_freem(mb_copy);
return NULL;
}
}
return mb_copy;
}
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 };
MGETHDR(mb_first, M_NOWAIT, 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;
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;
}
pimstat.pims_snd_registers_msgs++;
pimstat.pims_snd_registers_bytes += len;
return 0;
}
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;
}
MGETHDR(mb_first, M_NOWAIT, 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;
ip_outer = mtod(mb_first, struct ip *);
*ip_outer = pim_encap_iphdr;
ip_outer->ip_id = ip_newid();
ip_outer->ip_len = htons(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;
ip_outer->ip_tos = ip->ip_tos;
if (ip->ip_off & htons(IP_DF))
ip_outer->ip_off |= htons(IP_DF);
pimhdr = (struct pim_encap_pimhdr *)((caddr_t)ip_outer
+ sizeof(pim_encap_iphdr));
*pimhdr = pim_encap_pimhdr;
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, ntohs(ip_outer->ip_len));
pimstat.pims_snd_registers_msgs++;
pimstat.pims_snd_registers_bytes += len;
return 0;
}
int
pim_input(struct mbuf **mp, int *offp, int proto)
{
struct mbuf *m = *mp;
struct ip *ip = mtod(m, struct ip *);
struct pim *pim;
int minlen;
int datalen = ntohs(ip->ip_len);
int ip_tos;
int iphlen;
iphlen = *offp;
*mp = NULL;
pimstat.pims_rcv_total_msgs++;
pimstat.pims_rcv_total_bytes += datalen;
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(IPPROTO_DONE);
}
minlen = iphlen + (datalen >= PIM_REG_MINLEN ? PIM_REG_MINLEN : PIM_MINLEN);
if ((m->m_flags & M_EXT || m->m_len < minlen) &&
(m = m_pullup(m, minlen)) == NULL) {
log(LOG_ERR, "pim_input: m_pullup failure\n");
return(IPPROTO_DONE);
}
ip = mtod(m, struct ip *);
ip_tos = ip->ip_tos;
m->m_data += iphlen;
m->m_len -= iphlen;
pim = mtod(m, struct pim *);
if (PIM_VT_T(pim->pim_vt) == PIM_REGISTER && in_cksum(m, PIM_MINLEN) == 0) {
} 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(IPPROTO_DONE);
}
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(IPPROTO_DONE);
}
m->m_data -= iphlen;
m->m_len += iphlen;
if (PIM_VT_T(pim->pim_vt) == PIM_REGISTER) {
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(IPPROTO_DONE);
}
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(IPPROTO_DONE);
}
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));
}
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(IPPROTO_DONE);
}
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(IPPROTO_DONE);
}
if ((ntohl(*reghdr) & PIM_NULL_REGISTER))
goto pim_input_to_daemon;
if (encap_ip->ip_tos != ip_tos) {
encap_ip->ip_tos = ip_tos;
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);
m->m_data -= (iphlen + PIM_MINLEN);
m->m_len += (iphlen + PIM_MINLEN);
}
mcp = m_copym(m, 0, iphlen + PIM_REG_MINLEN, M_NOWAIT);
if (mcp == NULL) {
log(LOG_ERR,
"pim_input: pim register: could not copy register head\n");
m_freem(m);
return(IPPROTO_DONE);
}
pimstat.pims_rcv_registers_msgs++;
pimstat.pims_rcv_registers_bytes += ntohs(encap_ip->ip_len);
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);
m = mcp;
}
pim_input_to_daemon:
*mp = m;
*offp = iphlen;
rip_input(mp, offp, proto);
return(IPPROTO_DONE);
}
#endif
static int
ip_mroute_modevent(module_t mod, int type, void *unused)
{
switch (type) {
case MOD_LOAD:
lwkt_gettoken(&mroute_token);
ip_mcast_src = X_ip_mcast_src;
ip_mforward = X_ip_mforward;
ip_mrouter_done = X_ip_mrouter_done;
ip_mrouter_get = X_ip_mrouter_get;
ip_mrouter_set = X_ip_mrouter_set;
ip_rsvp_force_done = X_ip_rsvp_force_done;
ip_rsvp_vif = X_ip_rsvp_vif;
ipip_input = X_ipip_input;
legal_vif_num = X_legal_vif_num;
mrt_ioctl = X_mrt_ioctl;
rsvp_input_p = X_rsvp_input;
lwkt_reltoken(&mroute_token);
break;
case MOD_UNLOAD:
if (ip_mrouter)
return EINVAL;
lwkt_gettoken(&mroute_token);
ip_mcast_src = NULL;
ip_mforward = NULL;
ip_mrouter_done = NULL;
ip_mrouter_get = NULL;
ip_mrouter_set = NULL;
ip_rsvp_force_done = NULL;
ip_rsvp_vif = NULL;
ipip_input = NULL;
legal_vif_num = NULL;
mrt_ioctl = NULL;
rsvp_input_p = NULL;
lwkt_reltoken(&mroute_token);
break;
}
return 0;
}
static moduledata_t ip_mroutemod = {
"ip_mroute",
ip_mroute_modevent,
0
};
DECLARE_MODULE(ip_mroute, ip_mroutemod, SI_SUB_PSEUDO, SI_ORDER_ANY);