#include "opt_inet.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/in_cksum.h>
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/netisr.h>
#include <net/route.h>
#include <net/raw_cb.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/ip_gre.h>
#else
#error ip_gre input without IP?
#endif
#include <net/gre/if_gre.h>
#include <machine/stdarg.h>
#if 1
void gre_inet_ntoa(struct in_addr in);
#endif
static struct gre_softc *gre_lookup(struct mbuf *, u_int8_t);
static int gre_input2(struct mbuf *, int, u_char);
int
gre_input(struct mbuf **mp, int *offp, int proto)
{
struct mbuf *m;
int ret, off;
off = *offp;
m = *mp;
*mp = NULL;
proto = (mtod(m, struct ip *))->ip_p;
ret = gre_input2(m, off, proto);
if (ret == 0) {
*mp = m;
rip_input(mp, offp, proto);
}
return(IPPROTO_DONE);
}
static int
gre_input2(struct mbuf *m ,int hlen, u_char proto)
{
static const uint32_t af = AF_INET;
struct greip *gip = mtod(m, struct greip *);
int isr;
struct gre_softc *sc;
u_short flags;
if ((sc = gre_lookup(m, proto)) == NULL) {
return (0);
}
IFNET_STAT_INC(&sc->sc_if, ipackets, 1);
IFNET_STAT_INC(&sc->sc_if, ibytes, m->m_pkthdr.len);
switch (proto) {
case IPPROTO_GRE:
hlen += sizeof (struct gre_h);
flags = ntohs(gip->gi_flags);
if ((flags & GRE_CP) | (flags & GRE_RP))
hlen += 4;
if (flags & GRE_RP)
return(0);
if (flags & GRE_KP)
hlen += 4;
if (flags & GRE_SP)
hlen +=4;
switch (ntohs(gip->gi_ptype)) {
case ETHERTYPE_IP:
case WCCP_PROTOCOL_TYPE:
isr = NETISR_IP;
break;
case ETHERTYPE_IPV6:
default:
return(0);
}
break;
default:
return(0);
}
m->m_data += hlen;
m->m_len -= hlen;
m->m_pkthdr.len -= hlen;
if (sc->sc_if.if_bpf) {
bpf_gettoken();
if (sc->sc_if.if_bpf)
bpf_ptap(sc->sc_if.if_bpf, m, &af, sizeof(af));
bpf_reltoken();
}
m->m_pkthdr.rcvif = &sc->sc_if;
m->m_flags &= ~M_HASH;
netisr_queue(isr, m);
return(1);
}
int
gre_mobile_input(struct mbuf **mp, int *offp, int proto)
{
static const uint32_t af = AF_INET;
struct mbuf *m = *mp;
struct ip *ip = mtod(m, struct ip *);
struct mobip_h *mip = mtod(m, struct mobip_h *);
struct gre_softc *sc;
int msiz;
if ((sc = gre_lookup(m, IPPROTO_MOBILE)) == NULL) {
m_freem(m);
return(IPPROTO_DONE);
}
IFNET_STAT_INC(&sc->sc_if, ipackets, 1);
IFNET_STAT_INC(&sc->sc_if, ibytes, m->m_pkthdr.len);
if(ntohs(mip->mh.proto) & MOB_H_SBIT) {
msiz = MOB_H_SIZ_L;
mip->mi.ip_src.s_addr = mip->mh.osrc;
} else {
msiz = MOB_H_SIZ_S;
}
mip->mi.ip_dst.s_addr = mip->mh.odst;
mip->mi.ip_p = (ntohs(mip->mh.proto) >> 8);
if (gre_in_cksum((u_short*)&mip->mh,msiz) != 0) {
m_freem(m);
return(IPPROTO_DONE);
}
bcopy((caddr_t)(ip) + (ip->ip_hl << 2) + msiz, (caddr_t)(ip) +
(ip->ip_hl << 2), m->m_len - msiz - (ip->ip_hl << 2));
m->m_len -= msiz;
m->m_pkthdr.len -= msiz;
ip->ip_len = htons(ntohs(ip->ip_len) + sizeof(struct ip) - msiz);
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, (ip->ip_hl << 2));
if (sc->sc_if.if_bpf) {
bpf_gettoken();
if (sc->sc_if.if_bpf)
bpf_ptap(sc->sc_if.if_bpf, m, &af, sizeof(af));
bpf_reltoken();
}
m->m_pkthdr.rcvif = &sc->sc_if;
netisr_queue(NETISR_IP, m);
return(IPPROTO_DONE);
}
static struct gre_softc *
gre_lookup(struct mbuf *m, u_int8_t proto)
{
struct ip *ip = mtod(m, struct ip *);
struct gre_softc *sc;
for (sc = LIST_FIRST(&gre_softc_list); sc != NULL;
sc = LIST_NEXT(sc, sc_list)) {
if ((sc->g_dst.s_addr == ip->ip_src.s_addr) &&
(sc->g_src.s_addr == ip->ip_dst.s_addr) &&
(sc->g_proto == proto) &&
((sc->sc_if.if_flags & IFF_UP) != 0))
return (sc);
}
return (NULL);
}