/* Copyright (c) 2003,2004 Jeremy Kerr & Rusty Russell This file is part of nfsim. nfsim is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. nfsim is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with nfsim; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "ipv4.h" #include "utils.h" #include #include #if 0 #include #endif int route(struct sk_buff *skb); LIST_HEAD(routes); static struct notifier_block *inetaddr_chain; static int __ip_route_output_key(struct rtable **rp, struct flowi *flp); static int destroy_route(void *r) { struct ipv4_route *route = r; list_del(&route->entry); return 0; } void add_route_for_device(struct in_device *indev) { struct ipv4_route *route; route = talloc(indev, struct ipv4_route); talloc_set_destructor(route, destroy_route); route->netmask = indev->ifa_list->ifa_mask; route->network = indev->ifa_list->ifa_address & indev->ifa_list->ifa_mask; route->interface = indev->dev; route->gateway = indev->ifa_list->ifa_address; list_add_tail(&route->entry, &routes); } struct rtable *rcache; static int destroy_rtable(void *r) { struct rtable *rt = r, **p; /* Remove from cache if it's there (it should be, currently) */ for (p = &rcache; *p; p = &(*p)->u.rt_next) { if (*p == rt) { *p = rt->u.rt_next; break; } } return 0; } #if 0 /* need the following: - interfaces - routes */ static void init(void) { /* name our hooks */ nf_hooknames[PF_INET][0] = "NF_IP_PRE_ROUTING"; nf_hooknames[PF_INET][1] = "NF_IP_LOCAL_IN"; nf_hooknames[PF_INET][2] = "NF_IP_FORWARD"; nf_hooknames[PF_INET][3] = "NF_IP_LOCAL_OUT"; nf_hooknames[PF_INET][4] = "NF_IP_POST_ROUTING"; } init_call(init); #endif int register_inetaddr_notifier(struct notifier_block *nb) { return notifier_chain_register(&inetaddr_chain, nb); } int unregister_inetaddr_notifier(struct notifier_block *nb) { return notifier_chain_unregister(&inetaddr_chain, nb); } int __call_inetaddr_notifier(unsigned long val, struct in_ifaddr *ifa) { struct notifier_block *nb = inetaddr_chain; return notifier_call_chain(&nb, val, ifa); } #if 0 static int ip_rcv_finish(struct sk_buff *skb) { if (!skb->dst && ip_route_input(skb, skb->nh.iph->daddr, skb->nh.iph->saddr, skb->nh.iph->tos, skb->dev)) { kfree_skb(skb); return -1; } return dst_input(skb); } #endif int ip_rcv(struct sk_buff *skb) { #if 0 skb->dev->stats.rxpackets++; skb->dev->stats.rxbytes += skb->len; /* pull data to the start of the ip header */ skb_pull(skb, skb->nh.raw - skb->data); log_packet(skb, "rcv:%s", skb->dev->name); return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL, ip_rcv_finish); #else return 0; #endif } int ip_rcv_local(struct sk_buff *skb) { struct rtable *rt; struct flowi fl; log_packet(skb, "rcv_local"); /* pull data to the start of the ip header */ skb_pull(skb, skb->nh.raw - skb->data); rt = (struct rtable *)skb->dst; if (rt) goto routed; memset(&fl, 0, sizeof(fl)); fl.fl4_dst = skb->nh.iph->daddr; fl.fl4_src = skb->nh.iph->saddr; fl.fl4_tos = skb->nh.iph->tos; #ifdef CONFIG_IP_ROUTE_FWMARK fl.fl4_fwmark = skb->nfmark; #endif if (__ip_route_output_key(&rt, &fl)) { log_route(skb, "no route"); kfree_skb(skb); return 1; } skb->dst = (struct dst_entry *)rt; dst_hold(skb_dst); routed: skb->dev = skb->dst->dev; #if 0 return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, skb->dev, dst_output); #else return 0; #endif } static int ip_local_deliver(struct sk_buff *skb) { #if 0 return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL, nf_send_local); #else return 0; #endif } int ip_finish_output(struct sk_buff *skb) { struct net_device *dev = skb->dst->dev; skb->dev = dev; skb->protocol = htons(ETH_P_IP); /* if (skb->mac.ethernet) skb->mac.ethernet->h_proto = skb->protocol; */ #if 0 return NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev, nf_send); #else return 0; #endif } static int ip_output(struct sk_buff *skb) { /* FIXME: fragment? */ return ip_finish_output(skb); } unsigned short ip_compute_csum(unsigned char * buff, int len) { return csum_fold(csum_partial(buff, len, 0)); } static int __ip_route_output_key(struct rtable **rp, struct flowi *flp) { struct rtable *rth; struct net_device *dev; struct ipv4_route *route; if (should_i_fail(__func__)) return -ENOMEM; /* check for a cached route */ for (rth = rcache; rth; rth = rth->u.rt_next) { if (rth->fl.fl4_dst == flp->fl4_dst && rth->fl.fl4_src == flp->fl4_src && rth->fl.iif == 0 && rth->fl.oif == flp->oif && #ifdef CONFIG_IP_ROUTE_FWMARK rth->fl.fl4_fwmark == flp->fl4_fwmark && #endif rth->fl.fl4_tos == flp->fl4_tos) { dst_hold(&rth->u.dst) * rp = rth; return 0; } } list_for_each_entry(dev, &interfaces, entry) { struct in_ifaddr *ifaddr; if (!dev->ip_ptr) continue; ifaddr = ((struct in_device *)(dev->ip_ptr))->ifa_list; while (ifaddr) { if (flp->fl4_dst == ifaddr->ifa_local) { rth = talloc_zero(dev->ip_ptr, struct rtable); talloc_set_destructor(rth, destroy_rtable); rth->u.dst.output = ip_output; rth->u.dst.input = ip_local_deliver; rth->u.dst.dev = &loopback_dev; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) rth->u.dst.pmtu = 1500; #endif rth->rt_src = rth->fl.fl4_src = flp->fl4_src; rth->rt_dst = rth->fl.fl4_dst = flp->fl4_dst; rth->rt_gateway = flp->fl4_dst; rth->rt_iif = rth->fl.iif = flp->iif; rth->fl.fl4_tos = flp->fl4_tos; #ifdef CONFIG_IP_ROUTE_FWMARK rth->fl.fl4_fwmark = flp->fl4_fwmark; #endif rth->u.rt_next = rcache; rcache = rth; *rp = rth; return 0; } ifaddr = ifaddr->ifa_next; } } /* otherwise, find the appropriate route & create an rcache entry */ list_for_each_entry(route, &routes, entry) { if ((flp->fl4_dst & route->netmask) == route->network) { rth = talloc_zero(route, struct rtable); talloc_set_destructor(rth, destroy_rtable); rth->u.dst.dev = route->interface; rth->u.dst.output = ip_output; rth->u.dst.input = NULL; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) rth->u.dst.pmtu = 1500; #endif rth->rt_src = rth->fl.fl4_src = flp->fl4_src; if (!rth->rt_src) { rth->rt_src = inet_select_addr(route->interface, flp->fl4_dst, RT_SCOPE_UNIVERSE); } rth->rt_dst = rth->fl.fl4_dst = flp->fl4_dst; rth->rt_gateway = route->gateway; rth->fl.oif = route->interface->ifindex; rth->fl.oif = flp->oif; rth->fl.fl4_tos = flp->fl4_tos; #ifdef CONFIG_IP_ROUTE_FWMARK rth->fl.fl4_fwmark = flp->fl4_fwmark; #endif /* add to rcache list */ rth->u.rt_next = rcache; rcache = rth; *rp = rth; return 0; } } return 1; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) int ip_route_output_key(struct rtable **rp, struct rt_key *key) { struct flowi fl; fl.fl4_dst = key->dst; fl.fl4_src = key->src; fl.iif = 0; fl.oif = key->oif; #ifdef CONFIG_IP_ROUTE_FWMARK fl.fl4_fwmark = key->fwmark; #endif fl.fl4_tos = key->tos; return __ip_route_output_key(rp, &fl); } #else int ip_route_output_key(struct rtable **rp, struct flowi *flp) { return __ip_route_output_key(rp, flp); } #endif /* 2.6.10-rc3 added this. */ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, struct sock *sk, int flags) { struct rtable **rp = (void *)dst_p; return __ip_route_output_key(rp, fl); } #if 0 static int ip_forward(struct sk_buff *skb) { u32 check; if (!(--skb->nh.iph->ttl)) { log_route(skb, "ip_forward:ttl expired"); icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0); kfree_skb(skb); return 1; } nfsim_update_skb(skb, &skb->nh.iph->ttl, sizeof(skb->nh.iph->ttl)); check = skb->nh.iph->check; check += htons(0x0100); skb->nh.iph->check = check + (check >= 0xFFFF); /* Tell nfsim it's me changing data here. */ nfsim_update_skb(skb, &skb->nh.iph->check, sizeof(skb->nh.iph->check)); /* FIXME: strict source routing... */ #if 0 return NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, skb->dst->dev, dst_output); #else return 0; #endif } #endif #if 0 int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr, u8 tos, struct net_device *dev) { struct rtable *rth; struct ipv4_route *route; int iif = dev->ifindex; for (rth = rcache; rth; rth = rth->u.rt_next) { if (rth->fl.fl4_dst == daddr && rth->fl.fl4_src == saddr && rth->fl.iif == iif && rth->fl.oif == 0 && #ifdef CONFIG_IP_ROUTE_FWMARK rth->fl.fl4_fwmark == skb->nfmark && #endif rth->fl.fl4_tos == tos) { dst_hold(&rth->u.dst); skb->dst = (struct dst_entry*)rth; return 0; } } /* is this a local packet ? */ list_for_each_entry(dev, &interfaces, entry) { struct in_ifaddr *ifaddr; if (!dev->ip_ptr) continue; ifaddr = ((struct in_device *)(dev->ip_ptr))->ifa_list; while (ifaddr) { if (skb->nh.iph->daddr == ifaddr->ifa_local) { log_route(skb, "route:local packet (%s)", dev->name); rth = talloc_zero(dev->ip_ptr, struct rtable); talloc_set_destructor(rth, destroy_rtable); rth->u.dst.output = NULL; rth->u.dst.input = ip_local_deliver; rth->u.dst.dev = &loopback_dev; rth->rt_src = rth->fl.fl4_src = saddr; rth->rt_dst = rth->fl.fl4_dst = daddr; rth->rt_gateway = daddr; rth->rt_iif = rth->fl.iif = dev->ifindex; rth->fl.fl4_tos = tos; #ifdef CONFIG_IP_ROUTE_FWMARK rth->fl.fl4_fwmark = skb->nfmark; #endif skb->dst = &rth->u.dst; return 0; } ifaddr = ifaddr->ifa_next; } } /* otherwise, find the appropriate route & create an rcache entry */ list_for_each_entry(route, &routes, entry) { if ((skb->nh.iph->daddr & route->netmask) == route->network) { rth = talloc_zero(route, struct rtable); talloc_set_destructor(rth, destroy_rtable); rth->u.dst.dev = route->interface; rth->u.dst.output = ip_output; rth->u.dst.input = ip_forward; rth->rt_src = rth->fl.fl4_src = saddr; rth->rt_dst = rth->fl.fl4_dst = daddr; rth->rt_gateway = route->gateway; rth->rt_iif = rth->fl.iif = route->interface->ifindex; rth->fl.oif = 0; rth->fl.fl4_tos = tos; #ifdef CONFIG_IP_ROUTE_FWMARK rth->fl.fl4_fwmark = skb->nfmark; #endif /* add to rcache list */ rth->u.rt_next = rcache; rcache = rth; skb->dst = &rth->u.dst; return 0; } } log_route(skb, "ERROR: packet is not local and no matching " "route (dst=%u.%u.%u.%u)", ((unsigned char *)&daddr)[0], ((unsigned char *)&daddr)[1], ((unsigned char *)&daddr)[2], ((unsigned char *)&daddr)[3]); return 1; } #endif int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)) { log_packet(skb, "ip_fragment"); (*output)(skb); return 0; } struct fraglist { struct list_head list; struct sk_buff *frags[20]; }; static struct list_head fraglist[IP_DEFRAG_VS_FWD]; static void init_fraglist(void) { unsigned int i; for (i = 0; i < ARRAY_SIZE(fraglist); i++) INIT_LIST_HEAD(&fraglist[i]); } init_call(init_fraglist); #if 0 static struct sk_buff *gather_frag(struct fraglist *f, struct sk_buff *skb) { unsigned int i, len, off, max = 0; bool ended = false; char filled[70000] = { 0 }; char data[70000]; for (i = 0; i < ARRAY_SIZE(f->frags); i++) { if (!f->frags[i]) { if (!skb) break; f->frags[i] = skb; talloc_steal(f, skb); skb = NULL; } off = (ntohs(f->frags[i]->nh.iph->frag_off) & IP_OFFSET) * 8; len = ntohs(f->frags[i]->nh.iph->tot_len) - f->frags[i]->nh.iph->ihl * 4; if (len + off > max) max = len + off; memset(filled + off, 1, len); skb_copy_bits(f->frags[i], f->frags[i]->nh.iph->ihl * 4, data + off, len); if (!(ntohs(f->frags[i]->nh.iph->frag_off) & IP_MF)) ended = true; } if (skb) barf("Can't handle %i frags!\n", i); /* Not a complete packet? */ if (!ended || memchr(filled, 0, max) != NULL) return NULL; /* Copy header and data. */ suppress_failtest++; skb = skb_copy_expand(f->frags[0], 0, max, GFP_KERNEL); suppress_failtest--; skb->nh.iph = (void *)skb->data; memcpy(skb->nh.iph, f->frags[0]->nh.iph, f->frags[0]->nh.iph->ihl * 4); memcpy(skb_put(skb, max), data, max); /* Except we're not a fragment, and we're longer. */ skb->nh.iph->frag_off = 0; skb->nh.iph->tot_len = htons(max + skb->nh.iph->ihl * 4); list_del(&f->list); talloc_free(f); return skb; } static struct sk_buff *ip_defrag_user(struct sk_buff *skb, u32 user) { struct fraglist *i; list_for_each_entry(i, &fraglist[user], list) { if (i->frags[0]->nh.iph->saddr != skb->nh.iph->saddr || i->frags[0]->nh.iph->daddr != skb->nh.iph->daddr || i->frags[0]->nh.iph->protocol != skb->nh.iph->protocol) continue; return gather_frag(i, skb); } i = talloc(NULL, struct fraglist); memset(i->frags, 0, sizeof(i->frags)); i->frags[0] = skb; list_add(&i->list, &fraglist[user]); talloc_steal(i, skb); return NULL; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11) struct sk_buff *ip_defrag(struct sk_buff *skb) { return ip_defrag_user(skb, 0); } #else struct sk_buff *ip_defrag(struct sk_buff *skb, u32 user) { return ip_defrag_user(skb, user); } #endif void ipfrag_flush(void) { } #endif void icmp_send(struct sk_buff *skb_in, int type, int code, u32 info) { log_packet(skb_in, "icmp_send:type=%d, code=%d, info=%d", type, code, info); } void ip_send_check(struct iphdr *iph) { iph->check = 0; iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); } static inline unsigned short from32to16(unsigned long x) { /* add up 16-bit and 16-bit for 16+c bit */ x = (x & 0xffff) + (x >> 16); /* add up carry.. */ x = (x & 0xffff) + (x >> 16); return x; } static unsigned long do_csum(const unsigned char * buff, int len) { int odd, count; unsigned long result = 0; if (len <= 0) return 0; odd = 1 & (unsigned long) buff; if (odd) { result = *buff; len--; buff++; } count = len >> 1; /* nr of 16-bit words.. */ if (count) { if (2 & (unsigned long) buff) { result += *(const unsigned short *) buff; count--; len -= 2; buff += 2; } count >>= 1; /* nr of 32-bit words.. */ if (count) { unsigned long carry = 0; do { unsigned int w = *(const unsigned int *) buff; count--; buff += 4; result += carry; result += w; carry = (w > result); } while (count); result += carry; result = (result & 0xffff) + (result >> 16); } if (len & 2) { result += *(const unsigned short *) buff; buff += 2; } } if (len & 1) result += (*buff << 8); result = from32to16(result); if (odd) result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); return result; } unsigned short ip_fast_csum(void * iph, unsigned int ihl) { return ~do_csum(iph, ihl * 4); } unsigned int csum_fold(unsigned int sum) { return ~from32to16(sum); } #if 0 unsigned int csum_partial(const void * buff, int len, unsigned int sum) { unsigned int result = do_csum(buff, len); /* add in old sum, and carry.. */ result += sum; if (sum > result) result += 1; return result; } #endif u32 csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr, unsigned short len, unsigned short proto, unsigned int sum) { struct { u32 srcip, dstip; u8 mbz, protocol; u16 proto_len; } pseudo_header = { saddr, daddr, 0, proto, htons(len) }; return csum_partial(&pseudo_header, sizeof(pseudo_header), sum); } unsigned short csum_tcpudp_magic(unsigned long saddr, unsigned long daddr, unsigned short len, unsigned short proto, unsigned int sum) { return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum)); } uint16_t tcp_v4_check(struct tcphdr *th, int len, unsigned long saddr, unsigned long daddr, unsigned long base) { return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_TCP, base); } unsigned inet_addr_type(u32 addr) { struct net_device *dev; if (ZERONET(addr) || BADCLASS(addr)) return RTN_BROADCAST; if (MULTICAST(addr)) return RTN_MULTICAST; list_for_each_entry(dev, &interfaces, entry) { struct in_ifaddr *ifaddr; if (!dev->ip_ptr) continue; for (ifaddr = ((struct in_device *)(dev->ip_ptr))->ifa_list; ifaddr; ifaddr = ifaddr->ifa_next) if (ifaddr->ifa_local == addr) return RTN_LOCAL; } return RTN_UNICAST; } static __inline__ int inet_ifa_match(u32 addr, struct in_ifaddr *ifa) { return !((addr ^ ifa->ifa_address) & ifa->ifa_mask); } /* Not strictly correct for loopback packets: they ignore device if it * doesn't match. */ u32 inet_select_addr(const struct net_device *dev, u32 dst, int scope) { u32 addr = 0; struct in_device *in_dev; struct in_ifaddr *ifa; in_dev = dev->ip_ptr; if (!in_dev) return 0; for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { if (!dst || inet_ifa_match(dst, ifa)) { addr = ifa->ifa_local; break; } if (!addr) addr = ifa->ifa_local; } return addr; } int lastid = 4; void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, struct sock *sk) { iph->id = ++lastid; } void ip_select_ident(struct iphdr *iph, struct dst_entry *dst, struct sock *sk) { __ip_select_ident(iph, dst, sk); } static char *print_data(char *ptr, const char *data, int len) { static const char hexbuf[] = "0123456789abcdef"; int i; ptr += sprintf(ptr, "DATA "); for (i = 0; i < len; i++) { if (data[i] == '\n') { *ptr++ = '\\'; *ptr++ = 'n'; } else if (data[i] == '\r') { *ptr++ = '\\'; *ptr++ = 'r'; } else if (data[i] == '\0') { *ptr++ = '\\'; *ptr++ = '0'; } else if (data[i] == '\t') { *ptr++ = '\\'; *ptr++ = 't'; } else if (data[i] == '\\') { *ptr++ = '\\'; *ptr++ = '\\'; } else if (data[i] & 0x80 || (data[i] & 0xe0) == 0) { *ptr++ = '\\'; *ptr++ = 'x'; *ptr++ = hexbuf[(data[i] >> 4) & 0xf]; *ptr++ = hexbuf[data[i] & 0xf]; } else { *ptr++ = data[i]; } } *ptr = '\0'; return ptr; } static char *describe(char *ptr, int len, struct iphdr *iph, char *dump_flags) { /* the length of the packet reported by the ipv4 header */ int iplen; struct icmphdr *icmph; struct tcphdr *tcph; struct udphdr *udph; char *p; *ptr = '\0'; if (len < sizeof(struct iphdr)) { strcat(ptr, "-TRUNCATED-"); goto out; } if (csum_partial(iph, sizeof(struct iphdr), 0) != 0xFFFF) { strcat(ptr, "-BAD IP CSUM-"); goto out; } if (ntohs(iph->frag_off) & IP_OFFSET) ptr += sprintf(ptr, "FRAG=%u ", (ntohs(iph->frag_off) & IP_OFFSET) * 8); if (ntohs(iph->frag_off) & IP_DF) ptr += sprintf(ptr, "DF "); if (ntohs(iph->frag_off) & IP_MF) ptr += sprintf(ptr, "MF "); if (ntohs(iph->frag_off) & IP_CE) ptr += sprintf(ptr, "CE "); if (dump_flags && strstr(dump_flags, "ttl")) ptr += sprintf(ptr, "TTL=%i ", iph->ttl); if (dump_flags && strstr(dump_flags, "tos")) ptr += sprintf(ptr, "TOS=%i ", iph->tos); if (dump_flags && strstr(dump_flags, "dscp")) ptr += sprintf(ptr, "DSCP=0x%x ", (iph->tos >> IPT_DSCP_SHIFT)); if (dump_flags && strstr(dump_flags, "ect")) ptr += sprintf(ptr, "ECT=0x%x ", (iph->tos & 3)); ptr += sprintf(ptr, "%u.%u.%u.%u %u.%u.%u.%u ", ((unsigned char *)&iph->saddr)[0], ((unsigned char *)&iph->saddr)[1], ((unsigned char *)&iph->saddr)[2], ((unsigned char *)&iph->saddr)[3], ((unsigned char *)&iph->daddr)[0], ((unsigned char *)&iph->daddr)[1], ((unsigned char *)&iph->daddr)[2], ((unsigned char *)&iph->daddr)[3]); if (ntohs(iph->frag_off) & IP_OFFSET) goto out; iplen = htons(iph->tot_len) - sizeof(struct iphdr); len -= sizeof(struct iphdr); switch (iph->protocol) { case IPPROTO_ICMP: icmph = (struct icmphdr *)(iph + 1); ptr += sprintf(ptr, "%Zu %u ", iplen - sizeof(struct icmphdr), iph->protocol); if (len < sizeof(struct icmphdr)) { ptr += sprintf(ptr, "-TRUNCATED-"); goto out; } ptr += sprintf(ptr, "%u %u ", icmph->type, icmph->code); if (len < iplen) { ptr += sprintf(ptr, "-TRUNCATED-"); goto out; } if (!(ntohs(iph->frag_off) & IP_MF) && csum_partial((char *)icmph, iplen, 0) != 0xFFFF) { ptr += sprintf(ptr, "-BAD ICMP CSUM-"); goto out; } if (icmph->type == 0 || icmph->type == 8) { ptr += sprintf(ptr, "%u %u ", ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence)); } else { /* Print out packet inside it. */ ptr += sprintf(ptr, "CONTAINS "); ptr = describe(ptr, iplen - sizeof(struct icmphdr), (struct iphdr *)(icmph + 1), dump_flags); } break; case IPPROTO_UDP: udph = (struct udphdr *)(iph + 1); ptr += sprintf(ptr, "%Zu %u ", iplen - sizeof(struct udphdr), iph->protocol); if (len < sizeof(struct udphdr)) { ptr += sprintf(ptr, "-TRUNCATED-"); goto out; } ptr += sprintf(ptr, "%u %u ", ntohs(udph->source), ntohs(udph->dest)); if (len < iplen) { ptr += sprintf(ptr, "-TRUNCATED-"); goto out; } if (!(ntohs(iph->frag_off) & IP_MF) && udph->check && csum_tcpudp_magic(iph->saddr, iph->daddr, iplen, IPPROTO_UDP, csum_partial(udph, iplen, 0))) { ptr += sprintf(ptr, "-BAD UDP CSUM- (%04x)", udph->check); goto out; } if (dump_flags && strstr(dump_flags, "data")) ptr = print_data(ptr, (char *)(udph + 1), iplen - sizeof(*udph)); break; case IPPROTO_TCP: tcph = (struct tcphdr *)(iph + 1); if (len < sizeof(struct tcphdr)) { /* Assume no tcp options... */ ptr += sprintf(ptr, "%Zu %u ", iplen - sizeof(*tcph), iph->protocol); ptr += sprintf(ptr, "-TRUNCATED-"); goto out; } ptr += sprintf(ptr, "%u %u ", iplen - tcph->doff * 4, iph->protocol); ptr += sprintf(ptr, "%u %u ", ntohs(tcph->source), ntohs(tcph->dest)); if (len < iplen) { ptr += sprintf(ptr, "-TRUNCATED-"); goto out; } p = ptr; if (tcph->syn) ptr += sprintf(ptr, "SYN"); if (tcph->fin) ptr += sprintf(ptr, "%sFIN", ptr == p ? "" : "/"); if (tcph->rst) ptr += sprintf(ptr, "%sRST", ptr == p ? "" : "/"); if (tcph->ack) ptr += sprintf(ptr, "%sACK", ptr == p ? "" : "/"); if (tcph->urg) ptr += sprintf(ptr, "%sURG", ptr == p ? "" : "/"); if (tcph->psh) ptr += sprintf(ptr, "%sPSH", ptr == p ? "" : "/"); if (tcph->cwr) ptr += sprintf(ptr, "%sCWR", ptr == p ? "" : "/"); if (tcph->ece) ptr += sprintf(ptr, "%sECE", ptr == p ? "" : "/"); if (ptr == p) ptr += sprintf(ptr, "NONE"); ptr += sprintf(ptr, " "); if (tcph->seq) ptr += sprintf(ptr, "SEQ=%u ", ntohl(tcph->seq)); if (tcph->ack_seq) ptr += sprintf(ptr, "ACK=%u ", ntohl(tcph->ack_seq)); if (tcph->window) ptr += sprintf(ptr, "WIN=%u ", ntohs(tcph->window)); if (tcph->doff * 4 != sizeof(struct tcphdr)) { char sep = '='; int i; ptr += sprintf(ptr, "OPT"); for (i = sizeof(struct tcphdr); i < tcph->doff * 4; i++) { ptr += sprintf(ptr, "%c%u", sep, ((u_int8_t *)tcph)[i]); sep = ','; } ptr += sprintf(ptr, " "); } if (!(ntohs(iph->frag_off) & IP_MF) && csum_tcpudp_magic(iph->saddr, iph->daddr, iplen, IPPROTO_TCP, csum_partial(tcph, iplen, 0))) { ptr += sprintf(ptr, "-BAD TCP CSUM- (%04x)", tcph->check); goto out; } if (dump_flags && strstr(dump_flags, "data")) ptr = print_data(ptr, (char *)tcph + tcph->doff * 4, iplen - tcph->doff * 4); break; default: ptr += sprintf(ptr, "%u %u", iplen, iph->protocol); if (len < iplen) { ptr += sprintf(ptr, "-TRUNCATED-"); goto out; } } out: if (ptr[-1] == ' ') { ptr--; *ptr = '\0'; } return ptr; } #if 0 char *ipv4_describe_packet(struct sk_buff *skb) { static char ipv4_pbuf[1024]; char packet[skb->len]; if (skb_copy_bits(skb, 0, packet, skb->len) != 0) barf("skb_copy_bits failed"); describe(ipv4_pbuf, skb->len, (struct iphdr *)packet, field_value(skb, "dump_flags")); return ipv4_pbuf; } #endif