Files
Arquivotheca.Solaris-2.5/uts/common/inet/ip_mroute.c
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

1584 lines
40 KiB
C
Executable File

/*
* Copyright (c) 1992 by Sun Microsystems, Inc.
*/
#pragma ident "@(#)ip_mroute.c 1.14 94/11/07 SMI"
/*
* Procedures for the kernel part of DVMRP,
* a Distance-Vector Multicast Routing Protocol.
* (See RFC-1075.)
*
* Written by David Waitzman, BBN Labs, August 1988.
* Modified by Steve Deering, Stanford, February 1989.
* Modified by Mark J. Steiglitz, Stanford, May, 1991
* Modified by Van Jacobson, LBL, January 1993
*
* MROUTING 1.4
*/
/* TODO
* - TODO strlog
*/
#ifndef MI_HDRS
#include <sys/types.h>
#include <sys/stream.h>
#include <sys/dlpi.h>
#include <sys/stropts.h>
#include <sys/strlog.h>
#include <sys/systm.h>
#include <sys/tihdr.h>
#include <sys/tiuser.h>
#include <sys/ddi.h>
#include <sys/cmn_err.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <sys/sockio.h>
#include <net/route.h>
#include <netinet/in.h>
#include <inet/common.h>
#include <inet/mi.h>
#include <inet/nd.h>
#include <inet/arp.h>
#include <inet/mib2.h>
#include <inet/ip.h>
#include <inet/snmpcom.h>
#include <netinet/igmp.h>
#include <netinet/igmp_var.h>
#include <netinet/ip_mroute.h>
#include <inet/ip_multi.h>
#include <inet/ip_ire.h>
#else
#include <types.h>
#include <stream.h>
#include <dlpi.h>
#include <stropts.h>
#include <strlog.h>
#include <lsystm.h>
#include <tihdr.h>
#include <tiuser.h>
#include <param.h>
#include <socket.h>
#include <if.h>
#include <if_arp.h>
#include <sockio.h>
#include <route.h>
#include <in.h>
#include <common.h>
#include <mi.h>
#include <nd.h>
#include <arp.h>
#include <mib2.h>
#include <ip.h>
#include <igmp.h>
#include <igmp_var.h>
#include <ip_mroute.h>
#include <ip_multi.h>
#include <ip_ire.h>
#endif
extern mib2_ip_t ip_mib;
#define same(a1, a2) \
(bcmp((caddr_t)(a1), (caddr_t)(a2), IP_ADDR_LEN) == 0)
static int ip_mrouter_init(queue_t *q);
int ip_mrouter_done( void );
static int add_vif(struct vifctl *vifcp);
static int del_vif(vifi_t *vifip);
static int add_lgrp(struct lgrplctl *gcp);
static int del_lgrp(struct lgrplctl *gcp);
static int grplst_member(struct vif *vifp, u_long gaddr);
static u_long nethash(u_long addr);
static int add_mrt(struct mrtctl *mrtcp);
static int del_mrt(struct in_addr *origin);
static struct mrt *mrtfind(u_long origin);
static void phyint_send(ipha_t *ipha, mblk_t *mp, struct vif *vifp, u32 dst);
static void srcrt_send(ipha_t *ipha, mblk_t *mp, struct vif *vifp, u32 dst);
static void encap_send(ipha_t *ipha, mblk_t *mp, struct vif *vifp, u32 dst);
#define LOG
#ifndef LOG_DEBUG
#define USE_MY_LOG
#define LOG_DEBUG 0
#endif
/*
* Globals. All but ip_mrouter and ip_mrtproto could be static,
* except for netstat or debugging purposes.
*/
queue_t *ip_g_mrouter = nilp(queue_t);
int ip_mrtproto = IGMP_DVMRP; /* for netstat only */
static struct mrt *mrttable[MRTHASHSIZ];
static struct vif viftable[MAXVIFS];
struct mrtstat mrtstat;
#define ENCAP_TTL 64
/* prototype IP hdr for encapsulated packets */
static ipha_t multicast_encap_iphdr = {
IP_SIMPLE_HDR_VERSION,
0, /* tos */
sizeof(ipha_t), /* total length */
0, /* id */
0, /* frag offset */
ENCAP_TTL, IPPROTO_ENCAP,
0, /* checksum */
};
/*
* Private variables.
*/
static vifi_t numvifs = 0;
/*
* one-back cache used by multiencap_decap to locate a tunnel's vif
* given a datagram's src ip address.
*/
static u_long last_encap_src;
static struct vif *last_encap_vif;
static kmutex_t last_encap_lock; /* Protects the above */
static kmutex_t cache_lock; /* Protects the next three variables */
static struct mrt *cached_mrt = nilp(struct mrt);
static u_long cached_origin;
static u_long cached_originmask;
/*
* MT design:
* All access through ip_mrouter_cmd() are single-threaded by holding the
* write access on the ip module wide readers-writers lock. Since the
* the data path (ip_mforward) is holding the read side of the same
* lock the only concern is modifications to data in the data path.
* The data that gets modified is:
* cached_{mrt,origin,originmask}
* vipf->v_cached_{group,result}
* global statistics
* For the two first categories there are mutexes. One global for the
* global route cache and one per vif structure for the per-vif
* group cache.
*
* The statistics are currently not protected (They could
* go into the ill structure so that the queue synchronization would
* give of exclusive access for free.)
*/
#ifdef IP_DEBUG
void show_packet (char *msg, mblk_t *mp);
void dump_hdr_cksum(ipha_t *ipha);
void dump_cksum(mblk_t *mp, int off);
void dump_icmp_echo_pattern(mblk_t *mp, int off);
#endif /*IP_DEBUG*/
staticf void log();
/*
* Handle DVMRP setsockopt commands to modify the multicast routing tables.
*/
int
ip_mrouter_cmd(cmd, q, data, datalen)
int cmd;
queue_t *q;
char *data;
int datalen;
{
#ifdef lint
datalen = datalen;
#endif
if (cmd != DVMRP_INIT && q != ip_g_mrouter) return EACCES;
switch (cmd) {
case DVMRP_INIT: return ip_mrouter_init(q);
case DVMRP_DONE: return ip_mrouter_done();
case DVMRP_ADD_VIF: return add_vif ((struct vifctl *)ALIGN32(data));
case DVMRP_DEL_VIF: return del_vif ((vifi_t *)ALIGN32(data));
case DVMRP_ADD_LGRP: return add_lgrp((struct lgrplctl *)ALIGN32(data));
case DVMRP_DEL_LGRP: return del_lgrp((struct lgrplctl *)ALIGN32(data));
case DVMRP_ADD_MRT: return add_mrt ((struct mrtctl *)ALIGN32(data));
case DVMRP_DEL_MRT: return del_mrt ((struct in_addr *)ALIGN32(data));
default: return EOPNOTSUPP;
}
}
static int saved_ip_g_forward = -1;
/*
* Enable multicast routing
*/
static int
ip_mrouter_init(q)
queue_t *q;
{
ipc_t *ipc = (ipc_t *)q->q_ptr;
if (ip_g_mrouter != nilp(queue_t)) return EADDRINUSE;
ip_g_mrouter = q;
ipc->ipc_multi_router = 1;
mutex_init(&cache_lock, "IP multicast forwarding cache", MUTEX_DEFAULT, 0);
mutex_init(&last_encap_lock, "IP multicast decap lock", MUTEX_DEFAULT, 0);
/* In order for tunnels to work we have to turn ip_g_forward on */
if (!WE_ARE_FORWARDING) {
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG, "ip_mrouter_init: turning on forwarding");
#endif
saved_ip_g_forward = ip_g_forward;
ip_g_forward = IP_FORWARD_ALWAYS;
}
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG, "ip_mrouter_init");
#endif
return 0;
}
/*
* Disable multicast routing
*/
int
ip_mrouter_done()
{
vifi_t vifi;
int i;
ipc_t *ipc = (ipc_t *)ip_g_mrouter->q_ptr;
if (saved_ip_g_forward != -1) {
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG, "ip_mrouter_done: turning off forwarding");
#endif
ip_g_forward = saved_ip_g_forward;
saved_ip_g_forward = -1;
}
ipc->ipc_multi_router = 0;
/*
* Always clear cache when vifs change.
* No need to get last_encap_lock since we are running as a writer.
*/
last_encap_vif = 0;
last_encap_src = 0;
/*
* For each phyint in use, free its local group list and
* disable promiscuous reception of all IP multicasts.
*/
for (vifi = 0; vifi < numvifs; vifi++) {
if (viftable[vifi].v_lcl_addr != 0 &&
!(viftable[vifi].v_flags & VIFF_TUNNEL)) {
ipif_t *ipif;
struct grplst *grp, *next_grp;
/* LINTED lint confusion: next_grp used before set */
for (grp = viftable[vifi].v_lcl_groups; grp; grp = next_grp) {
next_grp = grp->gl_next;
mi_free((char *)grp);
}
ipif = viftable[vifi].v_ipif;
if (ipif) {
(void)ip_delmulti(INADDR_ANY, ipif);
}
}
}
bzero((caddr_t)viftable, sizeof(viftable));
numvifs = 0;
/*
* Free any multicast route entries.
*/
for (i = 0; i < MRTHASHSIZ; i++) {
struct mrt *mrt, *next_mrt;
/* LINTED lint confusion: next_mrt used before set */
for (mrt = mrttable[i]; mrt; mrt = next_mrt) {
next_mrt = mrt->mrt_next;
mi_free((char *)mrt);
}
}
bzero((caddr_t)mrttable, sizeof(mrttable));
cached_mrt = nilp(struct mrt);
mutex_destroy(&cache_lock);
mutex_destroy(&last_encap_lock);
ip_g_mrouter = nilp(queue_t);
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG, "ip_mrouter_done");
#endif
return 0;
}
/*
* Add a vif to the vif table
*/
static int
add_vif(vifcp)
register struct vifctl *vifcp;
{
register struct vif *vifp = viftable + vifcp->vifc_vifi;
ipif_t *ipif;
int error;
if (vifcp->vifc_vifi >= MAXVIFS) return EINVAL;
if (vifp->v_lcl_addr != 0) return EADDRINUSE;
/* Find the interface with the local address */
if (!(ipif = ipif_lookup_addr((u32)vifcp->vifc_lcl_addr.s_addr)))
return EADDRNOTAVAIL;
if (vifcp->vifc_flags & VIFF_TUNNEL) {
vifp->v_rmt_addr = vifcp->vifc_rmt_addr.s_addr;
}
else {
/* Make sure the interface supports multicast */
if ((ipif->ipif_flags & IFF_MULTICAST) == 0) {
return EOPNOTSUPP;
}
/* Enable promiscuous reception of all IP multicasts from the if */
error = ip_addmulti(INADDR_ANY, ipif);
if (error) {
return error;
}
}
vifp->v_flags = vifcp->vifc_flags;
vifp->v_threshold = vifcp->vifc_threshold;
vifp->v_lcl_addr = vifcp->vifc_lcl_addr.s_addr;
vifp->v_ipif = ipif;
mutex_init(&vifp->v_cache_lock, "IP Multicast group cache",
MUTEX_DEFAULT, 0);
/* Adjust numvifs up if the vifi is higher than numvifs */
if (numvifs <= vifcp->vifc_vifi) numvifs = vifcp->vifc_vifi + 1;
/*
* Always clear cache when vifs change.
* No need to get last_encap_lock since we are running as a writer.
*/
last_encap_vif = 0;
last_encap_src = 0;
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG, "add_vif #%d, lcladdr %x, %s %x, thresh %x",
vifcp->vifc_vifi,
(int)ntohl(vifcp->vifc_lcl_addr.s_addr),
(vifcp->vifc_flags & VIFF_TUNNEL) ? "rmtaddr" : "mask",
(int)ntohl(vifcp->vifc_rmt_addr.s_addr),
vifcp->vifc_threshold);
#endif
return 0;
}
/*
* Delete a vif from the vif table
*/
static int
del_vif(vifip)
vifi_t *vifip;
{
struct vif *vifp = viftable + *vifip;
vifi_t vifi;
if (*vifip >= numvifs) return EINVAL;
if (vifp->v_lcl_addr == 0) return EADDRNOTAVAIL;
if (!(vifp->v_flags & VIFF_TUNNEL)) {
ipif_t *ipif;
struct grplst *grp, *next_grp;
/* LINTED lint confusion: next_grp used before set */
for (grp = vifp->v_lcl_groups; grp; grp = next_grp) {
next_grp = grp->gl_next;
mi_free((char *)grp);
}
ipif = vifp->v_ipif;
if (ipif) {
(void)ip_delmulti(INADDR_ANY, ipif);
}
}
/*
* Always clear cache when vifs change.
* No need to get last_encap_lock since we are running as a writer.
*/
last_encap_vif = 0;
last_encap_src = 0;
mutex_destroy(&vifp->v_cache_lock);
bzero((caddr_t)vifp, sizeof (*vifp));
/* Adjust numvifs down */
for (vifi = numvifs; vifi != 0; vifi--) /* vifi is unsigned */
if (viftable[vifi - 1].v_lcl_addr != 0) break;
numvifs = vifi;
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG, "del_vif %d, numvifs %d", *vifip, numvifs);
#endif
return 0;
}
/*
* Add the multicast group in the lgrpctl to the list of local multicast
* group memberships associated with the vif indexed by gcp->lgc_vifi.
*/
static int
add_lgrp(gcp)
register struct lgrplctl *gcp;
{
register struct vif *vifp = viftable + gcp->lgc_vifi;
register struct grplst *grp, *prev_grp;
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG,"add_lgrp %x on %d",
(int)ntohl(gcp->lgc_gaddr.s_addr), gcp->lgc_vifi);
#endif
if (gcp->lgc_vifi >= numvifs) return EINVAL;
if (vifp->v_lcl_addr == 0 ||
(vifp->v_flags & VIFF_TUNNEL)) return EADDRNOTAVAIL;
/* try to find a group list block that has free space left */
for (grp = vifp->v_lcl_groups, prev_grp = nilp(struct grplst)
; grp && grp->gl_numentries == GRPLSTLEN
; prev_grp = grp, grp = grp->gl_next)
;
if (grp == nilp(struct grplst)) {
/* no group list block with free space was found */
grp = (struct grplst *)ALIGN32(mi_zalloc(GRPBLKSIZE));
if (grp == nilp(struct grplst)) {
return ENOBUFS;
}
if (prev_grp == nilp(struct grplst))
vifp->v_lcl_groups = grp;
else
prev_grp->gl_next = grp;
}
grp->gl_gaddr[grp->gl_numentries++] = gcp->lgc_gaddr.s_addr;
if (gcp->lgc_gaddr.s_addr == vifp->v_cached_group)
vifp->v_cached_result = 1;
return 0;
}
/*
* Delete the the local multicast group associated with the vif
* indexed by gcp->lgc_vifi.
* Does not pullup the values from other blocks in the list unless the
* current block is totally empty. This is a ~bug.
*/
static int
del_lgrp(gcp)
register struct lgrplctl *gcp;
{
register struct vif *vifp = viftable + gcp->lgc_vifi;
register u_long i;
register struct grplst *grp, *prev_grp = nilp(struct grplst);
int cnt;
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG,"del_lgrp %x on %d",
(int)ntohl(gcp->lgc_gaddr.s_addr), gcp->lgc_vifi);
#endif
if (gcp->lgc_vifi >= numvifs) return EINVAL;
if (vifp->v_lcl_addr == 0 ||
(vifp->v_flags & VIFF_TUNNEL)) return EADDRNOTAVAIL;
if (gcp->lgc_gaddr.s_addr == vifp->v_cached_group)
vifp->v_cached_result = 0;
/* for all group list blocks */
for (grp = vifp->v_lcl_groups; grp; prev_grp = grp, grp = grp->gl_next) {
/* for all group addrs in an block */
for (cnt = grp->gl_numentries, i = 0; i < cnt; i++)
/* if this is the addr to delete */
if (same(&gcp->lgc_gaddr, &grp->gl_gaddr[i])) {
grp->gl_numentries--;
cnt--;
if (grp->gl_numentries == 0) {
/* the block is now empty */
if (prev_grp) {
prev_grp->gl_next = grp->gl_next;
} else {
vifp->v_lcl_groups = grp->gl_next;
}
mi_free((char *)grp);
} else
/* move all other group addresses down one address */
for (; i < cnt; i++)
grp->gl_gaddr[i] = grp->gl_gaddr[i + 1];
return 0;
}
}
return EADDRNOTAVAIL; /* not found */
}
/*
* Return 1 if gaddr is a member of the local group list for vifp.
*/
static int
grplst_member(vifp, gaddr)
struct vif *vifp;
u_long gaddr;
{
register int i;
register u_long *gl;
register struct grplst *grp;
mrtstat.mrts_grp_lookups++;
mutex_enter(&vifp->v_cache_lock);
if (gaddr == vifp->v_cached_group) {
i = vifp->v_cached_result;
mutex_exit(&vifp->v_cache_lock);
return (i);
}
mutex_exit(&vifp->v_cache_lock);
mrtstat.mrts_grp_misses++;
for (grp = vifp->v_lcl_groups; grp; grp = grp->gl_next) {
for (gl = &grp->gl_gaddr[0], i = grp->gl_numentries;
i; gl++, i--) {
if (gaddr == *gl) {
mutex_enter(&vifp->v_cache_lock);
vifp->v_cached_group = gaddr;
vifp->v_cached_result = 1;
mutex_exit(&vifp->v_cache_lock);
return 1;
}
}
}
mutex_enter(&vifp->v_cache_lock);
vifp->v_cached_group = gaddr;
vifp->v_cached_result = 0;
mutex_exit(&vifp->v_cache_lock);
return 0;
}
/*
* A simple hash function: returns MRTHASHMOD of the low-order octet of
* the argument's network or subnet number.
*/
static u_long
nethash(addr)
u_long addr;
{
addr &= ip_net_mask((u32)addr);
return (MRTHASHMOD(addr));
}
/*
* Add an mrt entry
*/
static int
add_mrt(mrtcp)
struct mrtctl *mrtcp;
{
struct mrt *rt;
u_long hash;
if (rt = mrtfind(mrtcp->mrtc_origin.s_addr)) {
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG,"add_mrt update o %x m %x p %x c %x l %x",
(int)ntohl(mrtcp->mrtc_origin.s_addr),
(int)ntohl(mrtcp->mrtc_originmask.s_addr),
mrtcp->mrtc_parent, mrtcp->mrtc_children, mrtcp->mrtc_leaves);
#endif
/* Just update the route */
rt->mrt_parent = mrtcp->mrtc_parent;
VIFM_COPY(mrtcp->mrtc_children, rt->mrt_children);
VIFM_COPY(mrtcp->mrtc_leaves, rt->mrt_leaves);
return 0;
}
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG,"add_mrt o %x m %x p %x c %x l %x",
(int)ntohl(mrtcp->mrtc_origin.s_addr),
(int)ntohl(mrtcp->mrtc_originmask.s_addr),
mrtcp->mrtc_parent, mrtcp->mrtc_children, mrtcp->mrtc_leaves);
#endif
rt = (struct mrt *)ALIGN32(mi_alloc(sizeof(struct mrt), BPRI_LO));
if (rt == 0) {
return ENOBUFS;
}
/*
* insert new entry at head of hash chain
*/
rt->mrt_origin = mrtcp->mrtc_origin.s_addr;
rt->mrt_originmask = mrtcp->mrtc_originmask.s_addr;
rt->mrt_parent = mrtcp->mrtc_parent;
VIFM_COPY(mrtcp->mrtc_children, rt->mrt_children);
VIFM_COPY(mrtcp->mrtc_leaves, rt->mrt_leaves);
/* link into table */
hash = nethash(mrtcp->mrtc_origin.s_addr);
rt->mrt_next = mrttable[hash];
mrttable[hash] = rt;
return 0;
}
/*
* Delete an mrt entry
*/
static int
del_mrt(origin)
struct in_addr *origin;
{
register struct mrt *rt, *prev_rt;
register u_long hash = nethash(origin->s_addr);
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG,"del_mrt orig %x",
(int)ntohl(origin->s_addr));
#endif
for (prev_rt = rt = mrttable[hash]
; rt
; prev_rt = rt, rt = rt->mrt_next) {
if (origin->s_addr == rt->mrt_origin)
break;
}
if (!rt) {
return ESRCH;
}
if (rt == cached_mrt)
cached_mrt = nilp(struct mrt);
if (prev_rt != rt) { /* if moved past head of list */
prev_rt->mrt_next = rt->mrt_next;
} else /* delete head of list, it is in the table */
mrttable[hash] = rt->mrt_next;
mi_free((char *)rt);
return 0;
}
/*
* Find a route for a given origin IP address.
*/
static struct mrt *
mrtfind(origin)
u_long origin;
{
register struct mrt *rt;
register u_int hash;
mrtstat.mrts_mrt_lookups++;
mutex_enter(&cache_lock);
if (cached_mrt != nilp(struct mrt) &&
(origin & cached_originmask) == cached_origin) {
mutex_exit(&cache_lock);
return (cached_mrt);
}
mutex_exit(&cache_lock);
mrtstat.mrts_mrt_misses++;
hash = nethash(origin);
for (rt = mrttable[hash]; rt; rt = rt->mrt_next) {
if ((origin & rt->mrt_originmask) == rt->mrt_origin) {
mutex_enter(&cache_lock);
cached_mrt = rt;
cached_origin = rt->mrt_origin;
cached_originmask = rt->mrt_originmask;
mutex_exit(&cache_lock);
return (rt);
}
}
return nilp(struct mrt);
}
/*
* IP multicast forwarding function. This function assumes that the packet
* pointed to by "ip" has arrived on (or is about to be sent to) the interface
* pointed to by "ill", and the packet is to be relayed to other networks
* that have members of the packet's destination IP multicast group.
*
* The packet is returned unscathed to the caller, unless it is tunneled
* or erroneous, in which case a non-zero return value tells the caller to
* discard it.
*/
#define IP_HDR_LEN 20 /* # bytes of fixed IP header (excluding options) */
#define TUNNEL_LEN 12 /* # bytes of IP option for tunnel encapsulation */
int
ip_mforward(ill, ipha, mp)
ill_t *ill; /* Incomming physical interface */
ipha_t *ipha;
mblk_t *mp;
{
register struct mrt *rt;
register struct vif *vifp;
register vifi_t vifi;
register u_char *ipoptions;
u_long *ipopts;
u_long tunnel_src;
u_long dst;
#ifdef LOG
if (ip_mrtdebug > 1)
log(LOG_DEBUG, "ip_mforward: src %x, dst %x, ill %s",
(int)ntohl(ipha->ipha_src), (int)ntohl(ipha->ipha_dst),
ill->ill_name);
#endif
dst = ipha->ipha_dst;
/*
* Don't forward a packet with time-to-live of zero or one,
* or a packet destined to a local-only group. This will not
* drop packets arriving over a tunnel since we don't know
* the destination address yet. We do it here for performace
* and redo it in the tunnel case.
*/
if (CLASSD(dst) &&
(ipha->ipha_ttl <= 1 ||
ntohl(dst) <= (u_long)INADDR_MAX_LOCAL_GROUP)) {
#ifdef LOG
if (ip_mrtdebug > 1)
log(LOG_DEBUG, "ip_mforward: dropped ttl %d, dst 0x%x",
ipha->ipha_ttl, (int)ntohl(dst));
#endif
mp->b_prev = NULL;
return 0;
}
if ((tunnel_src = (u32)mp->b_prev) != 0) {
/*
* Packet arrived over encapsulation tunnel.
*/
mp->b_prev = NULL;
} else if ((ipha->ipha_version_and_hdr_length & 0xf) <
(u_long)(IP_HDR_LEN + TUNNEL_LEN) >> 2 ||
(ipoptions = (u_char *)(ipha + 1))[1] != IPOPT_LSRR ) {
/*
* Packet arrived via a physical interface.
*/
tunnel_src = 0;
}
else {
/*
* Packet arrived through a tunnel.
*
* A tunneled packet has a single NOP option and a two-element
* loose-source-and-record-route (LSRR) option immediately following
* the fixed-size part of the IP header. At this point in processing,
* the IP header should contain the following IP addresses:
*
* original source - in the source address field
* destination group - in the destination address field
* remote tunnel end-point - in the first element of LSRR
* one of this host's addrs - in the second element of LSRR
*
* NOTE: RFC-1075 would have the original source and remote tunnel
* end-point addresses swapped. However, that could cause
* delivery of ICMP error messages to innocent applications
* on intermediate routing hosts! Therefore, we hereby
* change the spec.
*/
u_long ip_len;
u32 sum;
/*
* Verify the checksum since ip_rput does not do this
*/
sum = ip_csum_hdr(ipha);
if (sum) {
BUMP_MIB(ip_mib.ipInCksumErrs);
return 1;
}
/*
* Verify that the tunnel options are well-formed.
*/
ipopts = (u_long *)ALIGN32(ipoptions);
if (ipoptions[0] != IPOPT_NOP ||
ipoptions[2] != 11 || /* LSRR option length */
ipoptions[3] != 8 || /* LSRR address pointer before being
* modified by ip_rput_options */
(tunnel_src = ipopts[1]) == 0) {
mrtstat.mrts_bad_tunnel++;
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG,
"ip_mforward: bad tunnel from %x (%x %x %x %x %x %x)",
(int)ntohl(ipha->ipha_src),
ipoptions[0], ipoptions[1], ipoptions[2], ipoptions[3],
(int)ntohl(ipopts[1]), (int)ntohl(ipopts[2]));
#endif
return 1;
}
/*
* NOTE copy the current next hop from the LSRR to the dstination field
* This is needed since ip_rput does not modify the packet before
* passing it here.
*/
dst = ipha->ipha_dst = ipopts[2];
/*
* Delete the tunnel options from the packet.
*/
ovbcopy((caddr_t)(ipoptions + TUNNEL_LEN), (caddr_t)ipoptions,
(unsigned)(mp->b_wptr - mp->b_rptr - (IP_HDR_LEN + TUNNEL_LEN)));
mp->b_wptr -= TUNNEL_LEN;
ip_len = ntohs(ipha->ipha_length);
ip_len -= TUNNEL_LEN;
ipha->ipha_length = htons(ip_len);
ipha->ipha_version_and_hdr_length -= TUNNEL_LEN >> 2;
/* Update the checksum */
ipha->ipha_hdr_checksum = 0;
ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
/*
* Don't forward a packet with time-to-live of zero or one,
* or a packet destined to a local-only group.
*/
if (ipha->ipha_ttl <= 1 ||
ntohl(dst) <= (u_long)INADDR_MAX_LOCAL_GROUP) {
#ifdef LOG
if (ip_mrtdebug > 1)
log(LOG_DEBUG, "ip_mforward: dropped ttl %d, dst 0x%x",
ipha->ipha_ttl, (int)ntohl(dst));
#endif
return (int)tunnel_src;
}
}
mrtstat.mrts_fwd_in++;
#ifdef LOG
if (ip_mrtdebug > 1)
log(LOG_DEBUG, "ip_mforward: arrived via a %s",
tunnel_src ? "tunnel" : "physical if");
#endif
/*
* Don't forward if we don't have a route for the packet's origin.
*/
if (!(rt = mrtfind(ipha->ipha_src))) {
mrtstat.mrts_no_route++;
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG, "ip_mforward: no route for %x",
(int)ntohl(ipha->ipha_src));
#endif
return (int)tunnel_src;
}
/*
* Don't forward if it didn't arrive from the parent vif for its origin.
* Notes: v_ipif is the physical interface for both tunnels and
* physical vifs, so the first part of the if catches wrong physical
* interface; v_rmt_addr is zero for non-tunneled packets so
* the 2nd part catches both packets that arrive via a tunnel
* that shouldn't and packets that arrive via the wrong tunnel.
*/
vifi = rt->mrt_parent;
if (viftable[vifi].v_ipif->ipif_ill != ill ||
viftable[vifi].v_rmt_addr != tunnel_src) {
#ifdef LOG
if (ip_mrtdebug > 1)
log(LOG_DEBUG, "ip_mforward: arrived on wrong if,addr (%s, 0x%x) - should be (%s, 0x%x)",
ill->ill_name,
(int)ntohl(tunnel_src),
viftable[vifi].v_ipif->ipif_ill->ill_name,
(int)ntohl(viftable[vifi].v_rmt_addr));
#endif
mrtstat.mrts_wrong_if++;
return (int)tunnel_src;
}
/*
* For each vif, decide if a copy of the packet should be forwarded.
* Forward if:
* - the ttl exceeds the vif's threshold AND
* - the vif is a child in the origin's route AND
* - ( the vif is not a leaf in the origin's route OR
* the destination group has members on the vif )
*
* (This might be speeded up with some sort of cache -- someday.)
*/
for (vifp = viftable, vifi = 0; vifi < numvifs; vifp++, vifi++) {
if (ipha->ipha_ttl > vifp->v_threshold &&
VIFM_ISSET(vifi, rt->mrt_children) &&
(!VIFM_ISSET(vifi, rt->mrt_leaves) ||
grplst_member(vifp, dst))) {
mrtstat.mrts_fwd_out++;
if (vifp->v_flags & VIFF_SRCRT)
srcrt_send(ipha, mp, vifp, dst);
else if (vifp->v_flags & VIFF_TUNNEL)
encap_send(ipha, mp, vifp, dst);
else
phyint_send(ipha, mp, vifp, dst);
}
}
return (int)tunnel_src;
}
static void
phyint_send(ipha, mp, vifp, dst)
ipha_t *ipha;
mblk_t *mp;
struct vif *vifp;
u32 dst;
{
register mblk_t *mp_copy;
ipif_t *ipif;
#ifdef lint
ipha = ipha;
#endif
ipif = vifp->v_ipif;
mp_copy = copymsg(mp); /* TODO could copy header and dup rest */
if (mp_copy == nilp(mblk_t)) {
mrtstat.mrts_fwd_drop++;
return;
}
/* Need to loop back to members on the outgoing interface */
if (ilm_lookup_exact(ipif, dst)) {
/* The packet is not yet reassembled thus we need to pass it
* to ip_rput_local for checksum verification and reassembly
* (and fanout the user stream.)
*/
mblk_t *mp_loop;
ire_t *ire;
#ifdef LOG
if (ip_mrtdebug > 1)
log(LOG_DEBUG, "phyint_send loopback");
#endif
mp_loop = copymsg(mp_copy);
ire = ire_lookup_exact(~0, IRE_BROADCAST, 0);
if (mp_loop && ire)
ip_rput_local(ipif->ipif_ill->ill_rq, mp_loop,
(ipha_t *)ALIGN32(mp_loop->b_rptr),
ire);
#ifdef LOG
else
log(LOG_DEBUG, "phyint_send: mp_loop 0x%x, ire 0x%x\n",
mp_loop, ire);
#endif
}
ip_rput_forward_multicast(dst, mp_copy, ipif);
#ifdef LOG
if (ip_mrtdebug > 1)
log(LOG_DEBUG, "phyint_send on vif %d", vifp-viftable);
#endif
}
/* ARGSUSED */
static void
srcrt_send(ipha, mp, vifp, dst)
ipha_t *ipha;
mblk_t *mp;
struct vif *vifp;
u32 dst;
{
mblk_t *mp_copy, *mp_opts;
ipha_t *ipha_copy;
u_char *cp;
u_long ip_len;
u32 orig_optlen; /* In bytes */
u32 sum;
/*
* Make sure that adding the tunnel options won't exceed the
* maximum allowed number of option bytes.
*/
if ((ipha->ipha_version_and_hdr_length & 0xf) >
(u_long)(60 - TUNNEL_LEN) >> 2) {
mrtstat.mrts_cant_tunnel++;
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG, "tunnel_send: no room for tunnel options, from %x",
(int)ntohl(ipha->ipha_src));
#endif
return;
}
/* Verify the checksum since this might not have been done yet */
sum = ip_csum_hdr(ipha);
if (sum) {
BUMP_MIB(ip_mib.ipInCksumErrs);
return;
}
mp_copy = copymsg(mp); /* TODO could copy header and dup rest */
if (mp_copy == nilp(mblk_t)) {
mrtstat.mrts_fwd_drop++;
return;
}
ipha_copy = (ipha_t *)ALIGN32(mp_copy->b_rptr);
ipha_copy->ipha_dst = vifp->v_rmt_addr;
/*
* Adjust the ip header length to account for the tunnel options.
*/
ip_len = ntohs(ipha_copy->ipha_length);
ip_len += TUNNEL_LEN;
ipha_copy->ipha_length = htons(ip_len);
ipha_copy->ipha_version_and_hdr_length += TUNNEL_LEN >> 2;
/* Prepare for checksum computation */
orig_optlen = (ipha->ipha_version_and_hdr_length -
(u8)((IP_VERSION << 4) + IP_SIMPLE_HDR_LENGTH_IN_WORDS)) << 2;
ipha_copy->ipha_hdr_checksum = 0;
/* We have to put all of the ip header in one mblk since ip_forward
* assumes this for checksum calculation.
*/
mp_opts = allocb(IP_HDR_LEN + TUNNEL_LEN + orig_optlen, BPRI_HI);
if (mp_opts == nilp(mblk_t)) {
freemsg(mp_copy);
mrtstat.mrts_fwd_drop++;
return;
}
mp_opts->b_datap->db_type = M_DATA;
/*
* 'Delete' the base ip header from the mp_copy chain
*/
mp_copy->b_rptr += IP_HDR_LEN;
/*
* Make mp_opts be the new head of the packet chain.
* Any options of the packet will be copied in after the tunnel options.
*/
mp_opts->b_cont = mp_copy;
mp_opts->b_wptr += IP_HDR_LEN + TUNNEL_LEN + orig_optlen;
/*
* Copy the base ip header from the mp_copy chain to the new head mblk
*/
bcopy((caddr_t)ipha_copy, (char *)mp_opts->b_rptr, IP_HDR_LEN);
/*
* Add the NOP and LSRR after the base ip header
*/
cp = mp_opts->b_rptr + IP_HDR_LEN;
*cp++ = IPOPT_NOP;
*cp++ = IPOPT_LSRR;
*cp++ = 11; /* LSRR option length */
*cp++ = 8; /* LSSR pointer to second element */
/* add local tunnel end-point */
*(u32 *)ALIGN32(cp) = vifp->v_lcl_addr;
cp += 4;
*(u32 *)ALIGN32(cp) = ipha->ipha_dst; /* destination group */
cp += 4;
/*
* Copy the original ip options from the mp_copy chain to the new head mblk
*/
bcopy((caddr_t)mp_copy->b_rptr, (char *)cp, orig_optlen);
/*
* Remove the original options from the mp_copy chain
*/
mp_copy->b_rptr += orig_optlen;
/* Update the checksum by recalculating it
*/
ipha_copy = (ipha_t *)ALIGN32(mp_opts->b_rptr);
ipha_copy->ipha_hdr_checksum = ip_csum_hdr(ipha_copy);
/* TODO should we handle machines with only tunnels?
* (We can handle machines with pt-pt links since these support
* IFF_MULTICAST thus for uniformity ... but hopefully multicast tunneling
* will go away.)
* If so we might want to loop back to members on the outgoing vif...
*/
/* Make it look like the packet just arrived by passing it to ip_rput
* which can handle M_BREAK messages provided that b_next contains the
* destination.
* Note: this requires that ip_g_forward is turned on. See ip_mrouter_init
*/
/* Allocate an empty M_BREAK to avoid problems with the unclean b_next */
mp = allocb(0, BPRI_HI);
if (!mp) {
BUMP_MIB(ip_mib.ipOutDiscards);
freemsg(mp_opts);
return;
}
mp->b_datap->db_type = M_BREAK;
mp->b_cont = mp_opts;
mp_opts->b_next = (mblk_t *)vifp->v_rmt_addr; /* ipha_copy->ipha_dst */
put(vifp->v_ipif->ipif_ill->ill_rq, mp);
#ifdef LOG
if (ip_mrtdebug > 1)
log(LOG_DEBUG, "tunnel_send on vif %d", vifp-viftable);
#endif
}
/* ARGSUSED */
static void
encap_send(ipha, mp, vifp, dst)
ipha_t *ipha;
mblk_t *mp;
struct vif *vifp;
u32 dst;
{
mblk_t *mp_copy;
ipha_t *ipha_copy;
u_long len;
len = ntohs(ipha->ipha_length);
/*
* copy the old packet & pullup it's IP header into the
* new mbuf so we can modify it. Try to fill the new
* mbuf since if we don't the ethernet driver will.
*/
mp_copy = allocb(32 + sizeof(multicast_encap_iphdr), BPRI_MED);
if (mp_copy == NULL)
return;
mp_copy->b_rptr += 32;
mp_copy->b_wptr = mp_copy->b_rptr + sizeof(multicast_encap_iphdr);
if ((mp_copy->b_cont = copymsg(mp)) == NULL) {
freeb(mp_copy);
return;
}
/*
* fill in the encapsulating IP header.
*/
ipha_copy = (ipha_t *)ALIGN32(mp_copy->b_rptr);
*ipha_copy = multicast_encap_iphdr;
ipha_copy->ipha_length = htons(len + sizeof (ipha_t));
ipha_copy->ipha_src = vifp->v_lcl_addr;
ipha_copy->ipha_dst = vifp->v_rmt_addr;
/*
* turn the encapsulated IP header back into a valid one.
*/
ipha = (ipha_t *)ALIGN32(mp_copy->b_cont->b_rptr);
ipha->ipha_ttl--;
ipha->ipha_hdr_checksum = 0;
ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
/*
* Feed into ip_wput which will set the ident field and
* checksum the encapsulating header.
*/
put(vifp->v_ipif->ipif_ill->ill_wq, mp_copy);
}
/*
* De-encapsulate a packet and feed it back through ip input (this
* routine is called whenever IP gets a packet with proto type
* IPPROTO_ENCAP and a local destination address).
*/
void
ip_mroute_decap(q, mp)
queue_t *q;
mblk_t *mp;
{
ipha_t *ipha = (ipha_t *)ALIGN32(mp->b_rptr);
ipha_t *ipha_encap;
register int hlen = IPH_HDR_LENGTH(ipha);
register struct vif *vifp;
u32 src;
/*
* dump the packet if it's not to a multicast destination or if
* we don't have an encapsulating tunnel with the source.
* Note: This code assumes that the remote site IP address
* uniquely identifies the tunnel (i.e., that this site has
* at most one tunnel with the remote site).
*/
ipha_encap = (ipha_t *)ALIGN32((char *)ipha + hlen);
if (!CLASSD(ipha_encap->ipha_dst)) {
++mrtstat.mrts_bad_tunnel;
freemsg(mp);
return;
}
src = ipha->ipha_src;
mutex_enter(&last_encap_lock);
if (src != last_encap_src) {
register struct vif *vife;
vifp = viftable;
vife = vifp + numvifs;
last_encap_src = src;
last_encap_vif = 0;
for ( ; vifp < vife; ++vifp)
if (vifp->v_rmt_addr == src) {
if ((vifp->v_flags & (VIFF_TUNNEL|VIFF_SRCRT))
== VIFF_TUNNEL)
last_encap_vif = vifp;
break;
}
}
if ((vifp = last_encap_vif) == 0) {
mutex_exit(&last_encap_lock);
mrtstat.mrts_bad_tunnel++;
freemsg(mp);
#ifdef LOG
if (ip_mrtdebug)
log(LOG_DEBUG, "ip_mforward: no tunnel with %u",
(int)ntohl(src));
#endif
return;
}
mutex_exit(&last_encap_lock);
/*
* Need to pass in the tunnel source to ip_mforward (so that it can
* verify that the packet arrived over the correct vif.)
* We use b_prev to pass this information. This is safe
* since the ip_rput either free's the packet or passes it
* to ip_mforward.
*/
mp->b_prev = (mblk_t *)src;
mp->b_rptr += hlen;
/*
* Feed back into ip_rput as an M_DATA.
*/
ip_rput(q, mp);
}
/*
* Remove all records with v_ipif == ipif
* Called when an interface goes away (stream closed)
*/
void
reset_mrt_vif_ipif(ipif)
ipif_t *ipif;
{
vifi_t vifi, tmp_vifi;
/* Can't check vifi >= 0 since vifi_t is unsigned! */
for (vifi = numvifs; vifi != 0; vifi--) {
tmp_vifi = vifi - 1 ;
if (viftable[tmp_vifi].v_ipif == ipif) {
(void)del_vif(&tmp_vifi);
}
}
}
#ifdef USE_MY_LOG
/* TODO log() not avail in SVr4 */
/*VARARGS2*/
staticf void
log(level, fmt, a,b,c,d,f,g,h,i,j,k,l)
int level;
char *fmt;
{
#ifdef lint
level = level;
#endif
printf(fmt, a,b,c,d,f,g,h,i,j,k,l);
printf("\n");
}
#endif
ip_mroute_stats(optp, mp)
struct opthdr *optp;
mblk_t *mp;
{
optp->level = EXPER_DVMRP;
optp->name = 0;
if (!snmp_append_data(mp, (char *)&mrtstat, sizeof(mrtstat))){
ip0dbg(("ip_mroute_stats: failed %d bytes\n",
sizeof(mrtstat)));
return 0;
}
return 1;
}
ip_mroute_vif(optp, mp)
struct opthdr *optp;
mblk_t *mp;
{
struct vifinfo vi;
vifi_t vifi;
struct grplst *grp;
int i;
int had_members;
optp->level = EXPER_DVMRP;
optp->name = EXPER_DVMRP_VIF;
for (vifi = 0; vifi < numvifs; vifi++) {
if (viftable[vifi].v_lcl_addr == 0)
continue;
vi.vifi_vifi = vifi;
vi.vifi_flags = viftable[vifi].v_flags;
vi.vifi_threshold = viftable[vifi].v_threshold;
vi.vifi_lcl_addr.s_addr = viftable[vifi].v_lcl_addr;
vi.vifi_rmt_addr.s_addr = viftable[vifi].v_rmt_addr;
if (viftable[vifi].v_flags & VIFF_TUNNEL) {
vi.vifi_grp_addr.s_addr = 0;
if (!snmp_append_data(mp, (char *)&vi, sizeof(vi))) {
ip0dbg(("ip_mroute_vif: failed %d bytes\n",
sizeof(vi)));
return 0;
}
continue;
}
had_members = 0;
/* for all group list mblk */
for (grp = viftable[vifi].v_lcl_groups; grp;
grp = grp->gl_next) {
/* for all group addrs in an mblk */
for (i = 0; i < grp->gl_numentries; i++) {
vi.vifi_grp_addr.s_addr = grp->gl_gaddr[i];
had_members++;
if (!snmp_append_data(mp, (char *)&vi,
sizeof(vi))) {
ip0dbg(("ip_mroute_vif: failed %d bytes\n",
sizeof(vi)));
return 0;
}
}
}
if (!had_members) {
vi.vifi_grp_addr.s_addr = 0;
if (!snmp_append_data(mp, (char *)&vi, sizeof(vi))) {
ip0dbg(("ip_mroute_vif: failed %d bytes\n",
sizeof(vi)));
return 0;
}
}
}
return 1;
}
ip_mroute_mrt(optp, mp)
struct opthdr *optp;
mblk_t *mp;
{
int i;
struct mrt *rt;
struct mrtctl mrtc;
optp->level = EXPER_DVMRP;
optp->name = EXPER_DVMRP_MRT;
/* Loop over all has buckets and their chains */
for (i = 0; i < MRTHASHSIZ; i++) {
for (rt = mrttable[i]; rt; rt = rt->mrt_next) {
mrtc.mrtc_origin.s_addr = rt->mrt_origin;
mrtc.mrtc_originmask.s_addr = rt->mrt_originmask;
mrtc.mrtc_parent = rt->mrt_parent;
VIFM_COPY(rt->mrt_children, mrtc.mrtc_children);
VIFM_COPY(rt->mrt_leaves, mrtc.mrtc_leaves);
if (!snmp_append_data(mp, (char *)&mrtc,
sizeof(mrtc))) {
ip0dbg(("ip_mroute_mrt: failed %d bytes\n",
sizeof(mrtc)));
return 0;
}
}
}
return 1;
}
#ifdef IP_DEBUG
void
dump_hdr_cksum(ipha)
ipha_t *ipha;
{
u32 sum;
u16 * uph = ((u16 *)ipha);
u32 u1;
int i;
u1 = ipha->ipha_version_and_hdr_length - (u8)((IP_VERSION << 4));
u1 *= 2;
sum = 0;
for (i = 0; i < u1; i++) {
printf(" %d: %x + %x = %x\n",
i, (int)sum, (int)uph[i], (int)(sum + uph[i]));
sum += uph[i];
}
sum = (sum & 0xFFFF) + (sum >> 16);
printf("fold: %x, ", (int)sum);
sum = ~(sum + (sum >> 16)) & 0xFFFF;
printf("%x\n", (int)sum);
}
void
dump_cksum(mp, off)
mblk_t *mp;
int off;
{
u32 sum;
u16 * uph;
int i;
if (off & 1) {
printf("dump_cksum: bad offset %d\n", off);
return;
}
sum = 0; i = 0;
for (; mp; mp = mp->b_cont) {
uph = (u16 *)ALIGN16(mp->b_rptr + off);
off = 0;
if ((mp->b_wptr - (u8 *)uph) & 0x1)
printf("dump_cksum: bad mblk length\n");
printf(" mblk len %d\n", mp->b_wptr - (u8 *)uph);
while (uph < (u16 *)ALIGN16(mp->b_wptr)) {
printf(" %d: %x + %x = %x\n",
i, (int)sum, (int)*uph, (int)(sum + *uph));
sum += *uph;
uph++;
i++;
}
}
sum = (sum & 0xFFFF) + (sum >> 16);
printf("fold: %x, ", (int)sum);
sum = ~(sum + (sum >> 16)) & 0xFFFF;
printf("%x\n", (int)sum);
}
void
dump_icmp_echo_pattern(mp, off)
mblk_t *mp;
int off;
{
u_char * uph;
u_long i;
i = 0;
i += 8; off += 8; /* Skip timestamp portion */
for (; mp; mp = mp->b_cont) {
u_char *mblkstart;
u_char *start = nilp(u_char); /* Start of block of wrong bytes */
int wrong; /* Amount wrong in case there it is a constant
offset */
int diff; /* 1 if there is a constant offset */
mblkstart = uph = mp->b_rptr + off;
if (uph > mp->b_wptr) {
off -= (mp->b_wptr - mp->b_rptr);
continue;
}
off = 0;
printf("dump_icmp_echo_pattern: mblk len %d\n",
mp->b_wptr - uph);
while (uph < mp->b_wptr) {
if (*uph != (i & 0xff)) {
if (!start) {
printf(" %d (%d): %x should be %x\n",
(int)i, (int)(uph - mblkstart),
(int)uph[0], (int)i&0xff);
start = uph;
wrong = uph[0] - (i & 0xff);
diff = 1;
} else if (*uph != ((wrong + i) & 0xff)) {
diff = 0;
}
} else {
printf(" %d bytes wrong:", uph - start);
if (diff)
printf(" by %d\n", wrong);
else
printf(" not a constant difference\n");
if (uph < start)
return;
start = nilp(u_char);
}
uph++;
i++;
}
if (start) {
printf(" %d bytes wrong:", uph - start);
if (diff)
printf(" by %d\n", wrong);
else
printf(" not a constant difference\n");
start = nilp(u_char);
}
}
}
void
show_packet (msg, mp)
char * msg;
mblk_t * mp;
{
reg ipha_t * ipha;
u32 optlen;
ipha = (ipha_t *)ALIGN32(mp->b_rptr);
if (msg && *msg)
printf("%s", msg);
if ((mp->b_wptr - mp->b_rptr) < sizeof (ipha_t)) {
printf("show_packet: runt packet\n");
return;
}
optlen = ipha->ipha_version_and_hdr_length -
(u8)((IP_VERSION << 4) + IP_SIMPLE_HDR_LENGTH_IN_WORDS);
if (optlen*4 + sizeof (ipha_t) > mp->b_wptr - mp->b_rptr) {
printf("show_packet: runt packet %d - %d\n",
optlen, mp->b_wptr - mp->b_rptr);
return;
}
printf("version %d hdr_length %d tos %d\n",
ipha->ipha_version_and_hdr_length >> 4,
ipha->ipha_version_and_hdr_length & 0xF,
ipha->ipha_type_of_service);
printf("length %d ident %d offset 0x%x\n",
ntohs(ipha->ipha_length), ntohs(ipha->ipha_ident),
(int)ntohs(ipha->ipha_fragment_offset_and_flags));
printf("ttl %d protocol %d checksum 0x%x\n",
ipha->ipha_ttl, ipha->ipha_protocol,
(int)ntohs(ipha->ipha_hdr_checksum));
printf("src 0x%x dst 0x%x\n", (int)ntohl(ipha->ipha_src),
(int)ntohl(ipha->ipha_dst));
if (optlen) {
u32 *up = (u32 *)&ipha[1];
printf("options ");
while (optlen != 0) {
printf("0x%x ", (int)*up++);
optlen--;
}
}
}
#endif /*IP_DEBUG*/