int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst) { NOREF(pvIfData); int rc = VINF_SUCCESS; ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis); if (pIfNet) { /* * Create a mbuf for the gather list and push it onto the wire. * * Note! If the interface is in the promiscuous mode we need to send the * packet down the stack so it reaches the driver and Berkeley * Packet Filter (see @bugref{5817}). */ if ((fDst & INTNETTRUNKDIR_WIRE) || vboxNetFltDarwinIsPromiscuous(pThis)) { mbuf_t pMBuf = vboxNetFltDarwinMBufFromSG(pThis, pSG); if (pMBuf) { errno_t err = ifnet_output_raw(pIfNet, PF_LINK, pMBuf); if (err) rc = RTErrConvertFromErrno(err); } else rc = VERR_NO_MEMORY; } /* * Create a mbuf for the gather list and push it onto the host stack. */ if (fDst & INTNETTRUNKDIR_HOST) { mbuf_t pMBuf = vboxNetFltDarwinMBufFromSG(pThis, pSG); if (pMBuf) { /* This is what IONetworkInterface::inputPacket does. */ unsigned const cbEthHdr = 14; mbuf_pkthdr_setheader(pMBuf, mbuf_data(pMBuf)); mbuf_pkthdr_setlen(pMBuf, mbuf_pkthdr_len(pMBuf) - cbEthHdr); mbuf_setdata(pMBuf, (uint8_t *)mbuf_data(pMBuf) + cbEthHdr, mbuf_len(pMBuf) - cbEthHdr); mbuf_pkthdr_setrcvif(pMBuf, pIfNet); /* will crash without this. */ errno_t err = ifnet_input(pIfNet, pMBuf, NULL); if (err) rc = RTErrConvertFromErrno(err); } else rc = VERR_NO_MEMORY; } vboxNetFltDarwinReleaseIfNet(pThis, pIfNet); } return rc; }
errno_t utun_pkt_input (struct utun_pcb *pcb, mbuf_t m) { errno_t result; protocol_family_t protocol = 0; mbuf_pkthdr_setrcvif(m, pcb->utun_ifp); if (m_pktlen(m) >= 4) { protocol = *(u_int32_t *)mbuf_data(m); bpf_tap_in(pcb->utun_ifp, DLT_NULL, m, 0, 0); } if (pcb->utun_flags & UTUN_FLAGS_NO_INPUT) { /* flush data */ mbuf_freem(m); return 0; } // quick exit for keepalive packets if (protocol == AF_UTUN && pcb->utun_flags & UTUN_FLAGS_CRYPTO) { if (utun_pkt_crypto_output(pcb, &m) == 0) { return 0; } printf("%s: utun_pkt_crypto_output failed, flags %x\n", __FUNCTION__, pcb->utun_flags); return EINVAL; } if (!pcb->utun_ext_ifdata_stats) { struct ifnet_stat_increment_param incs; bzero(&incs, sizeof(incs)); incs.packets_in = 1; incs.bytes_in = mbuf_pkthdr_len(m); result = ifnet_input(pcb->utun_ifp, m, &incs); } else { result = ifnet_input(pcb->utun_ifp, m, NULL); } if (result != 0) { ifnet_stat_increment_in(pcb->utun_ifp, 0, 0, 1); printf("%s - ifnet_input failed: %d\n", __FUNCTION__, result); mbuf_freem(m); } return 0; }
static errno_t ipsec_proto_input(ifnet_t interface, protocol_family_t protocol, mbuf_t m, __unused char *frame_header) { struct ip *ip; uint32_t af = 0; ip = mtod(m, struct ip *); if (ip->ip_v == 4) af = AF_INET; else if (ip->ip_v == 6) af = AF_INET6; mbuf_pkthdr_setrcvif(m, interface); bpf_tap_in(interface, DLT_NULL, m, &af, sizeof(af)); if (proto_input(protocol, m) != 0) m_freem(m); return 0; }
static errno_t utun_pkt_input (struct utun_pcb *pcb, mbuf_t m) { errno_t result; protocol_family_t protocol = 0; mbuf_pkthdr_setrcvif(m, pcb->utun_ifp); if (m_pktlen(m) >= (int32_t)UTUN_HEADER_SIZE(pcb)) { protocol = *(u_int32_t *)mbuf_data(m); bpf_tap_in(pcb->utun_ifp, DLT_NULL, m, 0, 0); } if (pcb->utun_flags & UTUN_FLAGS_NO_INPUT) { /* flush data */ mbuf_freem(m); return 0; } if (!pcb->utun_ext_ifdata_stats) { struct ifnet_stat_increment_param incs; bzero(&incs, sizeof(incs)); incs.packets_in = 1; incs.bytes_in = mbuf_pkthdr_len(m); result = ifnet_input(pcb->utun_ifp, m, &incs); } else { result = ifnet_input(pcb->utun_ifp, m, NULL); } if (result != 0) { ifnet_stat_increment_in(pcb->utun_ifp, 0, 0, 1); printf("%s - ifnet_input failed: %d\n", __FUNCTION__, result); mbuf_freem(m); } return 0; }
/* * Do a remote procedure call (RPC) and wait for its reply. * If from_p is non-null, then we are doing broadcast, and * the address from whence the response came is saved there. */ int krpc_call( struct sockaddr_in *sa, u_int sotype, u_int prog, u_int vers, u_int func, mbuf_t *data, /* input/output */ struct sockaddr_in *from_p) /* output */ { socket_t so; struct sockaddr_in *sin; mbuf_t m, nam, mhead; struct rpc_call *call; struct rpc_reply *reply; int error, timo, secs; size_t len; static u_int32_t xid = ~0xFF; u_int16_t tport; size_t maxpacket = 1<<16; /* * Validate address family. * Sorry, this is INET specific... */ if (sa->sin_family != AF_INET) return (EAFNOSUPPORT); /* Free at end if not null. */ nam = mhead = NULL; /* * Create socket and set its recieve timeout. */ if ((error = sock_socket(AF_INET, sotype, 0, 0, 0, &so))) goto out1; { struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; if ((error = sock_setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))) goto out; } /* * Enable broadcast if necessary. */ if (from_p && (sotype == SOCK_DGRAM)) { int on = 1; if ((error = sock_setsockopt(so, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)))) goto out; } /* * Bind the local endpoint to a reserved port, * because some NFS servers refuse requests from * non-reserved (non-privileged) ports. */ if ((error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_SONAME, &m))) goto out; sin = mbuf_data(m); bzero(sin, sizeof(*sin)); mbuf_setlen(m, sizeof(*sin)); sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; sin->sin_addr.s_addr = INADDR_ANY; tport = IPPORT_RESERVED; do { tport--; sin->sin_port = htons(tport); error = sock_bind(so, (struct sockaddr*)sin); } while (error == EADDRINUSE && tport > IPPORT_RESERVED / 2); mbuf_freem(m); m = NULL; if (error) { printf("bind failed\n"); goto out; } /* * Setup socket address for the server. */ if ((error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_SONAME, &nam))) goto out; sin = mbuf_data(nam); mbuf_setlen(nam, sa->sin_len); bcopy((caddr_t)sa, (caddr_t)sin, sa->sin_len); if (sotype == SOCK_STREAM) { struct timeval tv; tv.tv_sec = 60; tv.tv_usec = 0; error = sock_connect(so, mbuf_data(nam), MSG_DONTWAIT); if (error && (error != EINPROGRESS)) goto out; error = sock_connectwait(so, &tv); if (error) { if (error == EINPROGRESS) error = ETIMEDOUT; printf("krpc_call: error waiting for TCP socket connect: %d\n", error); goto out; } } /* * Prepend RPC message header. */ m = *data; *data = NULL; #if DIAGNOSTIC if ((mbuf_flags(m) & MBUF_PKTHDR) == 0) panic("krpc_call: send data w/o pkthdr"); if (mbuf_pkthdr_len(m) < mbuf_len(m)) panic("krpc_call: pkthdr.len not set"); #endif len = sizeof(*call); if (sotype == SOCK_STREAM) len += 4; /* account for RPC record marker */ mhead = m; if ((error = mbuf_prepend(&mhead, len, MBUF_WAITOK))) goto out; if ((error = mbuf_pkthdr_setrcvif(mhead, NULL))) goto out; /* * Fill in the RPC header */ if (sotype == SOCK_STREAM) { /* first, fill in RPC record marker */ u_int32_t *recmark = mbuf_data(mhead); *recmark = htonl(0x80000000 | (mbuf_pkthdr_len(mhead) - 4)); call = (struct rpc_call *)(recmark + 1); } else { call = mbuf_data(mhead); } bzero((caddr_t)call, sizeof(*call)); xid++; call->rp_xid = htonl(xid); /* call->rp_direction = 0; */ call->rp_rpcvers = htonl(2); call->rp_prog = htonl(prog); call->rp_vers = htonl(vers); call->rp_proc = htonl(func); /* call->rp_auth = 0; */ /* call->rp_verf = 0; */ /* * Send it, repeatedly, until a reply is received, * but delay each re-send by an increasing amount. * If the delay hits the maximum, start complaining. */ timo = 0; for (;;) { struct msghdr msg; /* Send RPC request (or re-send). */ if ((error = mbuf_copym(mhead, 0, MBUF_COPYALL, MBUF_WAITOK, &m))) goto out; bzero(&msg, sizeof(msg)); if (sotype == SOCK_STREAM) { msg.msg_name = NULL; msg.msg_namelen = 0; } else { msg.msg_name = mbuf_data(nam); msg.msg_namelen = mbuf_len(nam); } error = sock_sendmbuf(so, &msg, m, 0, 0); if (error) { printf("krpc_call: sosend: %d\n", error); goto out; } m = NULL; /* Determine new timeout. */ if (timo < MAX_RESEND_DELAY) timo++; else printf("RPC timeout for server " IP_FORMAT "\n", IP_LIST(&(sin->sin_addr.s_addr))); /* * Wait for up to timo seconds for a reply. * The socket receive timeout was set to 1 second. */ secs = timo; while (secs > 0) { size_t readlen; if (m) { mbuf_freem(m); m = NULL; } if (sotype == SOCK_STREAM) { int maxretries = 60; struct iovec aio; aio.iov_base = &len; aio.iov_len = sizeof(u_int32_t); bzero(&msg, sizeof(msg)); msg.msg_iov = &aio; msg.msg_iovlen = 1; do { error = sock_receive(so, &msg, MSG_WAITALL, &readlen); if ((error == EWOULDBLOCK) && (--maxretries <= 0)) error = ETIMEDOUT; } while (error == EWOULDBLOCK); if (!error && readlen < aio.iov_len) { /* only log a message if we got a partial word */ if (readlen != 0) printf("short receive (%ld/%ld) from server " IP_FORMAT "\n", readlen, sizeof(u_int32_t), IP_LIST(&(sin->sin_addr.s_addr))); error = EPIPE; } if (error) goto out; len = ntohl(len) & ~0x80000000; /* * This is SERIOUS! We are out of sync with the sender * and forcing a disconnect/reconnect is all I can do. */ if (len > maxpacket) { printf("impossible packet length (%ld) from server " IP_FORMAT "\n", len, IP_LIST(&(sin->sin_addr.s_addr))); error = EFBIG; goto out; } do { readlen = len; error = sock_receivembuf(so, NULL, &m, MSG_WAITALL, &readlen); } while (error == EWOULDBLOCK); if (!error && (len > readlen)) { printf("short receive (%ld/%ld) from server " IP_FORMAT "\n", readlen, len, IP_LIST(&(sin->sin_addr.s_addr))); error = EPIPE; } } else { len = maxpacket; readlen = len; bzero(&msg, sizeof(msg)); msg.msg_name = from_p; msg.msg_namelen = (from_p == NULL) ? 0 : sizeof(*from_p); error = sock_receivembuf(so, &msg, &m, 0, &readlen); } if (error == EWOULDBLOCK) { secs--; continue; } if (error) goto out; len = readlen; /* Does the reply contain at least a header? */ if (len < MIN_REPLY_HDR) continue; if (mbuf_len(m) < MIN_REPLY_HDR) continue; reply = mbuf_data(m); /* Is it the right reply? */ if (reply->rp_direction != htonl(RPC_REPLY)) continue; if (reply->rp_xid != htonl(xid)) continue; /* Was RPC accepted? (authorization OK) */ if (reply->rp_astatus != 0) { error = ntohl(reply->rp_u.rpu_errno); printf("rpc denied, error=%d\n", error); /* convert rpc error to errno */ switch (error) { case RPC_MISMATCH: error = ERPCMISMATCH; break; case RPC_AUTHERR: error = EAUTH; break; } goto out; } if (mbuf_len(m) < REPLY_SIZE) { error = RPC_SYSTEM_ERR; } else { error = ntohl(reply->rp_u.rpu_ok.rp_rstatus); } /* Did the call succeed? */ if (error != 0) { printf("rpc status=%d\n", error); /* convert rpc error to errno */ switch (error) { case RPC_PROGUNAVAIL: error = EPROGUNAVAIL; break; case RPC_PROGMISMATCH: error = EPROGMISMATCH; break; case RPC_PROCUNAVAIL: error = EPROCUNAVAIL; break; case RPC_GARBAGE: error = EINVAL; break; case RPC_SYSTEM_ERR: error = EIO; break; } goto out; } goto gotreply; /* break two levels */ } /* while secs */ } /* forever send/receive */ error = ETIMEDOUT; goto out; gotreply: /* * Pull as much as we can into first mbuf, to make * result buffer contiguous. Note that if the entire * result won't fit into one mbuf, you're out of luck. * XXX - Should not rely on making the entire reply * contiguous (fix callers instead). -gwr */ #if DIAGNOSTIC if ((mbuf_flags(m) & MBUF_PKTHDR) == 0) panic("krpc_call: received pkt w/o header?"); #endif len = mbuf_pkthdr_len(m); if (sotype == SOCK_STREAM) len -= 4; /* the RPC record marker was read separately */ if (mbuf_len(m) < len) { if ((error = mbuf_pullup(&m, len))) goto out; reply = mbuf_data(m); } /* * Strip RPC header */ len = sizeof(*reply); if (reply->rp_u.rpu_ok.rp_auth.rp_atype != 0) { len += ntohl(reply->rp_u.rpu_ok.rp_auth.rp_alen); len = (len + 3) & ~3; /* XXX? */ } mbuf_adj(m, len); /* result */ *data = m; out: sock_close(so); out1: if (nam) mbuf_freem(nam); if (mhead) mbuf_freem(mhead); return error; }
errno_t kn_tcp_pkt_from_params(mbuf_t *data, u_int8_t tcph_flags, u_int32_t iph_saddr, u_int32_t iph_daddr, u_int16_t tcph_sport, u_int16_t tcph_dport, u_int32_t tcph_seq, u_int32_t tcph_ack, const char* payload, size_t payload_len) { int retval = 0; size_t tot_data_len, tot_buf_len, max_len; // mac osx thing.. to be safe, leave out 14 bytes for ethernet header. void *buf = NULL; struct ip* o_iph; struct tcphdr* o_tcph; u_int16_t csum; mbuf_csum_request_flags_t csum_flags = 0; boolean_t pkt_allocated = FALSE; tot_data_len = sizeof(struct ip) + sizeof(struct tcphdr) + payload_len; tot_buf_len = tot_data_len + ETHHDR_LEN; // allocate the packet retval = mbuf_allocpacket(MBUF_DONTWAIT, tot_buf_len, NULL, data); if (retval != 0) { kn_debug("mbuf_allocpacket returned error %d\n", retval); goto FAILURE; } else { pkt_allocated = TRUE; } max_len = mbuf_maxlen(*data); if (max_len < tot_buf_len) { kn_debug("no enough buffer space, try to request more.\n"); retval = mbuf_prepend(data, tot_buf_len - max_len, MBUF_DONTWAIT); if (retval != 0) { kn_debug("mbuf_prepend returned error %d\n", retval); goto FAILURE; } } mbuf_pkthdr_setlen(*data, tot_data_len); retval = mbuf_pkthdr_setrcvif(*data, NULL); if (retval != 0) { kn_debug("mbuf_pkthdr_setrcvif returned error %d\n", retval); goto FAILURE; } mbuf_setlen(*data, tot_data_len); retval = mbuf_setdata(*data, (mbuf_datastart(*data) + ETHHDR_LEN), tot_data_len); if (retval != 0) { kn_debug("mbuf_setdata returned error %d\n", retval); goto FAILURE; } buf = mbuf_data(*data); mbuf_pkthdr_setheader(*data, buf); o_iph = (struct ip*)buf; memset(o_iph, 0, sizeof(struct ip)); // setup IPv4 header o_iph->ip_hl = sizeof(struct ip) / 4; o_iph->ip_v = 4; o_iph->ip_tos = 0; o_iph->ip_id = 0; o_iph->ip_off = htons(IP_DF); o_iph->ip_p = IPPROTO_TCP; o_iph->ip_len = htons(tot_data_len); o_iph->ip_sum = 0; o_iph->ip_ttl = 64; o_iph->ip_src.s_addr = iph_saddr; o_iph->ip_dst.s_addr = iph_daddr; o_tcph = (struct tcphdr*)((char*)o_iph + sizeof(struct ip)); memset(o_tcph, 0, sizeof(struct tcphdr)); o_tcph->th_sport = tcph_sport; o_tcph->th_dport = tcph_dport; o_tcph->th_seq = tcph_seq; o_tcph->th_ack = tcph_ack; o_tcph->th_flags = tcph_flags; o_tcph->th_win = 0xffffU; o_tcph->th_off = sizeof(struct tcphdr) / 4; o_tcph->th_sum = 0; o_tcph->th_urp = 0; if (payload_len > 0) { memcpy((char*)o_tcph + sizeof(struct tcphdr), payload, payload_len); } mbuf_clear_csum_performed(*data); csum_flags |= MBUF_CSUM_REQ_IP; retval = mbuf_get_csum_requested(*data, &csum_flags, NULL); if (retval != 0) { kn_debug("mbuf_get_csum_requested returned error %d\n", retval); goto FAILURE; } /* calculate TCP checksum */ csum = kn_tcp_sum_calc(sizeof(struct tcphdr) + payload_len, (u_int16_t*)&o_iph->ip_src.s_addr, (u_int16_t*)&o_iph->ip_dst.s_addr, (u_int16_t*)o_tcph); o_tcph->th_sum = csum; return 0; FAILURE: if (pkt_allocated == TRUE) { mbuf_free(*data); } return retval; }
// Temporarily stuff a vlan tag back into a packet so that tag shows up to bpf. // We do it by creating a temp header mbuf with the enet/vlan header in it and // then point its next field to the proper place (after the dest+src addresses) in the original // mbuf. void IOEthernetInterface::_fixupVlanPacket(mbuf_t mt, u_int16_t vlan_tag, int inputPacket) { mbuf_t newmb; mbuf_t chain; size_t remainingBytes; size_t copyBytes = 0; //initialize to prevent annoying, incorrect warning that it's used uninitialized char * destptr; if( mbuf_gethdr(MBUF_DONTWAIT, MT_DATA, &newmb) ) return; //init enough of the mbuf to keep bpf happy mbuf_setlen(newmb, ETHER_ADDR_LEN*2 + VLAN_HEADER_LEN); mbuf_pkthdr_setlen(newmb, mbuf_pkthdr_len( mt ) + VLAN_HEADER_LEN); mbuf_pkthdr_setrcvif(newmb, mbuf_pkthdr_rcvif( mt ) ); //now walk the incoming mbuf to copy out its dst & src address and //locate the type/len field in the packet. chain = mt; remainingBytes = ETHER_ADDR_LEN*2; destptr = (char *)mbuf_data( newmb ); while(chain && remainingBytes) { copyBytes = remainingBytes > mbuf_len( chain ) ? mbuf_len( chain ): remainingBytes; remainingBytes -= copyBytes; bcopy( mbuf_data( chain ), destptr, copyBytes); destptr += copyBytes; if (mbuf_len( chain ) == copyBytes) //we've completely drained this mbuf { chain = mbuf_next( chain ); //advance to next copyBytes = 0; //if we break out of loop now, make sure the offset is correct } } // chain points to the mbuf that contains the packet data with type/len field // and copyBytes indicates the offset it's at. if(chain==0 || remainingBytes) { mbuf_freem( newmb ); return; //if we can't munge the packet, just return } //patch mbuf so its data points after the dst+src address mbuf_setdata(chain, (char *)mbuf_data( chain ) + copyBytes, mbuf_len( chain ) - copyBytes ); //finish setting up our head mbuf *(short *)(destptr) = htons(ETHERTYPE_VLAN); //vlan magic number *(short *)(destptr + 2) = htons( vlan_tag ); // and the tag's value mbuf_setnext( newmb, chain ); //stick it infront of the rest of the packet // feed the tap if(inputPacket) super::feedPacketInputTap( newmb ); else super::feedPacketOutputTap( newmb ); //release the fake header mbuf_setnext( newmb, NULL ); mbuf_freem( newmb ); //and repair our old mbuf mbuf_setdata( chain, (char *)mbuf_data( chain ) - copyBytes, mbuf_len( chain ) + copyBytes ); }
/* * Decapsulate. Does the real work and is called from in_gre_input() * (above) or ipv4_infilter(), Returns an mbuf back if packet is not * yet processed, and NULL if it needs no further processing. * proto is the protocol number of the "calling" foo_input() routine. */ mbuf_t in_gre_input(mbuf_t m, int hlen) { struct greip *gip; struct gre_softc *sc; u_int16_t flags; //static u_int32_t af; //u_int8_t proto; //proto = ((struct ip *)mbuf_data(m))->ip_p; if ((sc = gre_lookup(m, IPPROTO_GRE)) == NULL) { /* No matching tunnel or tunnel is down. */ return m; } /* from here on, we increased the sc->sc_refcnt, so do remember to decrease it before return */ if (mbuf_len(m) < sizeof(struct greip)) { mbuf_pullup(&m, sizeof(struct greip)); if (m == NULL) goto done; } gip = mbuf_data(m); //switch (proto) { // case IPPROTO_GRE: hlen += sizeof(struct gre_h); /* process GRE flags as packet can be of variable len */ flags = ntohs(gip->gi_flags); /* Checksum & Offset are present */ if ((flags & GRE_CP) | (flags & GRE_RP)) hlen += 4; /* We don't support routing fields (variable length) */ if (flags & GRE_RP) goto done; if (flags & GRE_KP) hlen += 4; if (flags & GRE_SP) hlen += 4; switch (ntohs(gip->gi_ptype)) { /* ethertypes */ case WCCP_PROTOCOL_TYPE: if (sc->wccp_ver == WCCP_V2) hlen += 4; /* FALLTHROUGH */ case ETHERTYPE_IP: //af = AF_INET; break; case ETHERTYPE_IPV6: //af = AF_INET6; break; //case ETHERTYPE_AT: // af = AF_APPLETALK; // break; default: /* Others not yet supported. */ goto done; } // break; // default: /* Others not yet supported. */ // goto done; //} if (hlen > mbuf_pkthdr_len(m)) { /* not a valid GRE packet */ mbuf_freem(m); m = NULL; goto done; } /* Unlike NetBSD, in FreeBSD(as well as Darwin) m_adj() adjusts mbuf_pkthdr_len(m) as well */ mbuf_adj(m, hlen); mbuf_pkthdr_setrcvif(m, sc->sc_ifp); mbuf_pkthdr_setheader(m, NULL); //mbuf_pkthdr_setheader(m, &af); /* it's ugly... */ struct ifnet_stat_increment_param incs; bzero(&incs, sizeof(incs)); incs.packets_in = 1; incs.bytes_in = mbuf_pkthdr_len(m); ifnet_input(sc->sc_ifp, m, &incs); m = NULL; /* ifnet_input() has freed the mbuf */ done: /* since we got sc->sc_refcnt add by one, we decrease it when done */ gre_sc_release(sc); return m; }
/* * input routine for IPPRPOTO_MOBILE * This is a little bit diffrent from the other modes, as the * encapsulating header was not prepended, but instead inserted * between IP header and payload */ mbuf_t in_mobile_input(mbuf_t m, int hlen) { #ifdef DEBUG printf("%s: got packet\n", __FUNCTION__); #endif struct ip *ip; struct mobip_h *mip; struct gre_softc *sc; int msiz; if ((sc = gre_lookup(m, IPPROTO_MOBILE)) == NULL) { /* No matching tunnel or tunnel is down. */ return m; } /* from here on, we increased the sc->sc_refcnt, so do remember to decrease it before return */ if (mbuf_len(m) < sizeof(*mip)) { mbuf_pullup(&m, sizeof(*mip)); if (m == NULL) goto done; } ip = mbuf_data(m); mip = mbuf_data(m); 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; if (mbuf_len(m) < (ip->ip_hl << 2) + msiz) { mbuf_pullup(&m, (ip->ip_hl << 2) + msiz); if (m == NULL) goto done; ip = mbuf_data(m); mip = mbuf_data(m); } mip->mi.ip_dst.s_addr = mip->mh.odst; mip->mi.ip_p = (ntohs(mip->mh.proto) >> 8); if (gre_in_cksum((u_int16_t *)&mip->mh, msiz) != 0) { mbuf_freem(m); m = NULL; goto done; } bcopy((caddr_t)(ip) + (ip->ip_hl << 2) + msiz, (caddr_t)(ip) + (ip->ip_hl << 2), mbuf_len(m) - msiz - (ip->ip_hl << 2)); mbuf_setdata(m, mbuf_data(m), mbuf_len(m) - msiz); mbuf_pkthdr_adjustlen(m, - msiz); /* * On FreeBSD, rip_input() supplies us with ip->ip_len * already converted into host byteorder and also decreases * it by the lengh of IP header, however, ip_input() expects * that this field is in the original format (network byteorder * and full size of IP packet), so that adjust accordingly. */ ip->ip_len = htons(ip->ip_len + sizeof(struct ip) - msiz); ip->ip_sum = 0; ip->ip_sum = in_cksum(m, (ip->ip_hl << 2)); mbuf_pkthdr_setrcvif(m, sc->sc_ifp); mbuf_pkthdr_setheader(m, NULL); struct ifnet_stat_increment_param incs; bzero(&incs, sizeof(incs)); incs.packets_in = 1; incs.bytes_in = mbuf_pkthdr_len(m); ifnet_input(sc->sc_ifp, m, &incs); m = NULL; /* ifnet_input() has freed the mbuf */ done: /* since we got sc->sc_refcnt add by one, we decrease it when done */ gre_sc_release(sc); return m; }