/* * Copyright 1988 Silicon Graphics, Inc. All rights reserved. * * Internet Protocol (IP), defined in RFCs 791 and 760. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef __sgi #include #endif #include "cache.h" #include "debug.h" #include "enum.h" #include "heap.h" #include "protodefs.h" #include "snooper.h" #include "protocols/ether.h" #include "protocols/ip.h" char ipname[] = "ip"; /* * IP field identifiers and descriptors. */ enum ipfid { V, HL, TOS, LEN, ID, OFF, TTL, P, SUM, SRC=IPFID_SRC, DST=IPFID_DST, OPT, OPTLEN, RTOFF, RTHOP, TSPTR, TSOFLW, TSFLG, TSTIME, TSADDR }; #define IPF_ISOPTION(pf) ((pf)->pf_id >= (int) OPT) #define PFI_OINT(name, title, id, type, off, level) \ PFI(name, title, id, DS_ZERO_EXTEND, sizeof(type), off, EXOP_NUMBER, \ level) #define PFI_OBIT(name, title, id, bits, off, level) \ PFI(name, title, id, DS_ZERO_EXTEND, -(bits), off, EXOP_NUMBER, level) static ProtoField ipfields[] = { PFI_UBIT("v", "Version", V, 4, PV_VERBOSE), PFI_UBIT("hl", "Header Length", HL, 4, PV_VERBOSE), PFI_UINT("tos", "Type of Service", TOS, u_char, PV_VERBOSE), PFI_UINT("len", "Total Length", LEN, u_short, PV_DEFAULT), PFI_UINT("id", "Identification", ID, u_short, PV_DEFAULT), PFI_UINT("off", "Fragment Offset", OFF, u_short, PV_DEFAULT), PFI_UINT("ttl", "Time to Live", TTL, u_char, PV_VERBOSE), PFI_UINT("p", "Protocol", P, u_char, PV_VERBOSE), PFI_UINT("sum", "Header Checksum", SUM, u_short, PV_VERBOSE), PFI_UINT("src", "Source Address", SRC, u_long, PV_TERSE), PFI_UINT("dst", "Destination Address", DST, u_long, PV_TERSE), PFI_OINT("opt", "Option Type", OPT, u_char, 0, PV_DEFAULT), PFI_OINT("optlen", "Option Length", OPTLEN, u_char, 1, PV_VERBOSE), PFI_OINT("rtoff", "Route Offset", RTOFF, u_char, 2, PV_VERBOSE), PFI_OINT("rthop", "Route Address", RTHOP, u_long, 0, PV_TERSE), PFI_OINT("tsptr", "Timestamp Pointer", TSPTR, u_char, 2, PV_VERBOSE), PFI_OBIT("tsoflw", "Timestamp Overflow", TSOFLW, 4, 24,PV_VERBOSE), PFI_OBIT("tsflg", "Timestamp Flag", TSFLG, 4, 28,PV_VERBOSE), PFI_OINT("tstime", "Timestamp Time", TSTIME, n_long, 0, PV_DEFAULT), PFI_OINT("tsaddr", "Timestamp Address", TSADDR, u_long, 0, PV_TERSE), }; #define IPFID(pf) ((enum ipfid) (pf)->pf_id) #define IPFIELD(fid) ipfields[(int) fid] /* * Nicknames for IP-based protocols. */ static ProtoMacro ipnicknames[] = { PMI("IP", ipname), PMI("bootp", "ip.udp.bootp"), PMI("BOOTP", "ip.udp.bootp"), PMI("hello", "ip.hello"), PMI("HELLO", "ip.hello"), PMI("icmp", "ip.icmp"), PMI("ICMP", "ip.icmp"), PMI("igmp", "ip.igmp"), PMI("IGMP", "ip.igmp"), PMI("rip", "ip.udp.rip"), PMI("RIP", "ip.udp.rip"), PMI("tcp", "ip.tcp"), PMI("TCP", "ip.tcp"), PMI("udp", "ip.udp"), PMI("UDP", "ip.udp"), PMI("dns", "ip.udp.dns"), PMI("DNS", "ip.udp.dns"), PMI("sunrpc", "ip.udp.sunrpc"), PMI("SUNRPC", "ip.udp.sunrpc"), PMI("tftp", "ip.udp.tftp"), PMI("TFTP", "ip.udp.tftp"), PMI("nfs", "ip.udp.sunrpc.nfs"), PMI("NFS", "ip.udp.sunrpc.nfs"), PMI("snmp", "ip.udp.snmp"), PMI("SNMP", "ip.udp.snmp"), PMI("ftp", "ip.tcp.ftp"), PMI("FTP", "ip.tcp.ftp"), PMI("telnet", "ip.tcp.telnet"), PMI("TELNET", "ip.tcp.telnet"), PMI("smtp", "ip.tcp.smtp"), PMI("SMTP", "ip.tcp.smtp"), PMI("netbios", "ip.tcp.netbios"), PMI("NETBIOS", "ip.tcp.netbios"), PMI("tsp", "ip.udp.tsp"), PMI("TSP", "ip.udp.tsp"), PMI("timed", "ip.udp.tsp"), PMI("TIMED", "ip.udp.tsp"), PMI("x11", "ip.tcp.x11"), PMI("X11", "ip.tcp.x11"), PMI("rlogin", "ip.tcp.rlogin"), PMI("RLOGIN", "ip.tcp.rlogin"), PMI("rcp", "ip.tcp.rcp"), PMI("RCP", "ip.tcp.rcp"), }; /* * Enumerated IP option type. */ static Enumeration ipopt; static Enumerator ipoptvec[] = { EI_VAL("EOL", IPOPT_EOL), EI_VAL("NOP", IPOPT_NOP), EI_VAL("RR", IPOPT_RR), EI_VAL("TS", IPOPT_TS), EI_VAL("SECURITY", IPOPT_SECURITY), EI_VAL("LSRR", IPOPT_LSRR), EI_VAL("SATID", IPOPT_SATID), EI_VAL("SSRR", IPOPT_SSRR), }; /* * IP constants and macros. */ static Enumerator ipconsts[] = { EI_VAL("MINHL", IP_MINHDRLEN), EI_VAL("DF", IP_DF), EI_VAL("MF", IP_MF), EI_VAL("MSS", IP_MSS), EI_VAL("MAXPRIVPORT", IPPORT_RESERVED-1), EI_VAL("BROADCAST", INADDR_BROADCAST), }; static ProtoMacro ipmacros[] = { PMI("between", "src == $1 && dst == $2 || dst == $1 && src == $2"), PMI("host", "src == $1 || dst == $1"), }; /* * IP protocol options. */ static ProtOptDesc ipoptdesc[] = { POD("etherupdate", IP_PROPT_ETHERUPDATE, "Update Ethernet hostname/address cache"), POD("hostbyname", IP_PROPT_HOSTBYNAME, "Decode IP addresses into hostnames"), POD("hostresorder", IP_PROPT_HOSTRESORDER, "Hostname resolution order; see resolver(4)"), }; /* * IP protocol interface. */ DefineProtocol(ip, ipname, "Internet Protocol", PRID_IP, DS_BIG_ENDIAN, IP_MAXHDRLEN, &IPFIELD(P), 0, ipoptdesc, lengthof(ipoptdesc), 0); /* * IP fragment matching and decoding caches. */ #define MAXFRAGS 16 /* maximum fragment cache size */ static Cache *ipfragmatches; static Cache *ipfragdecodes; static unsigned int ip_fraghash(struct ipfragkey *ipk) { return ipk->ipk_id ^ ipk->ipk_src.s_addr; } static int ip_fragcmp(struct ipfragkey *ipk1, struct ipfragkey *ipk2) { return ipk1->ipk_id != ipk2->ipk_id || ipk1->ipk_prototype != ipk2->ipk_prototype || ipk1->ipk_src.s_addr != ipk2->ipk_src.s_addr || ipk1->ipk_dst.s_addr != ipk2->ipk_dst.s_addr; } static void ip_fragmatchdump(struct ipfragkey *ipk, Expr *ex) { printf("(%u,%u,%s,%s) -> ", ipk->ipk_id, ipk->ipk_prototype, ip_hostname(ipk->ipk_src, IP_HOST), ip_hostname(ipk->ipk_dst, IP_HOST)); switch (ex->ex_op) { case EXOP_NUMBER: printf("%ld\n", ex->ex_val); break; case EXOP_ADDRESS: printf("%x:%x:%x:%x:%x:%x:%x:%x\n", ex->ex_addr.a_vec[0], ex->ex_addr.a_vec[1], ex->ex_addr.a_vec[2], ex->ex_addr.a_vec[3], ex->ex_addr.a_vec[4], ex->ex_addr.a_vec[5], ex->ex_addr.a_vec[6], ex->ex_addr.a_vec[7]); break; case EXOP_STRING: printf("\"%s\" (%d)\n", ex->ex_str.s_ptr, ex->ex_str.s_len); } } static void ip_fragdecodedump(struct ipfragkey *ipk, ProtoDecodeState *pds) { printf("(%u,%u,%s,%s) -> (%u,%s)\n", ipk->ipk_id, ipk->ipk_prototype, ip_hostname(ipk->ipk_src, IP_HOST), ip_hostname(ipk->ipk_dst, IP_HOST), pds->pds_mysize, pds->pds_proto->pr_name); } static struct cacheops ipfragmatchops = { { ip_fraghash, ip_fragcmp, xdr_matchresult }, 0, ip_fragmatchdump }; static struct cacheops ipfragdecodeops = { { ip_fraghash, ip_fragcmp, xdr_decodestate }, 0, ip_fragdecodedump }; /* * IP protocol functions. */ /* ARGSUSED */ int ip_func_badsum(Expr *argv, DataStream *ds, ProtoStack *ps, Expr *rex) { struct ip *ip; int hdrlen, optlen; u_short realsum, idealsum; ip = (struct ip *) ds_inline(ds, sizeof *ip, IP_HDRGRAIN); if (ip == 0) return 0; hdrlen = IP_HDRLEN(ip->ip_hl); optlen = hdrlen - IP_MINHDRLEN; if (optlen > ds->ds_count) return 0; realsum = ip->ip_sum; ip->ip_sum = 0; (void) ip_checksum((char *) ip, hdrlen, &idealsum); ip->ip_sum = realsum; rex->ex_op = EXOP_NUMBER; rex->ex_val = (ntohs(realsum) != idealsum); return 1; } static ProtoFuncDesc ipfunctions[] = { PFD("badsum", ip_func_badsum, 0, "Match packet if header checksum is incorrect"), }; /* * IP protocol operations. */ static Index *ipprotos; static int ip_init() { /* * Register IP as a known protocol. */ if (!pr_register(&ip_proto, ipfields, lengthof(ipfields), lengthof(ipoptvec) + lengthof(ipconsts) + lengthof(ipmacros) + lengthof(ipfunctions) + 12)) { return 0; } /* * Nest IP in loopback, Ethernet, LLC, etc. */ (void) pr_nest(&ip_proto, PRID_LOOP, AF_INET, ipnicknames, lengthof(ipnicknames)); (void) pr_nest(&ip_proto, PRID_ETHER, ETHERTYPE_IP, ipnicknames, lengthof(ipnicknames)); (void) pr_nest(&ip_proto, PRID_LLC, ETHERTYPE_IP, ipnicknames, lengthof(ipnicknames)); #ifdef __sgi (void) pr_nest(&ip_proto, PRID_CRAYIO, CY_IPLINK, ipnicknames, lengthof(ipnicknames)); #endif /* * Always define symbols, in case some were redefined as macros. */ en_init(&ipopt, ipoptvec, lengthof(ipoptvec), &ip_proto); pr_addnumbers(&ip_proto, ipconsts, lengthof(ipconsts)); pr_addmacros(&ip_proto, ipmacros, lengthof(ipmacros)); pr_addfunctions(&ip_proto, ipfunctions, lengthof(ipfunctions)); /* * Initialize or reinitialize the fragment and protocol hash tables. */ c_create("ip.fragmatches", MAXFRAGS, sizeof(struct ipfragkey), MAXTTL, &ipfragmatchops, &ipfragmatches); c_create("ip.fragdecodes", MAXFRAGS, sizeof(struct ipfragkey), MAXTTL, &ipfragdecodeops, &ipfragdecodes); in_create(3, sizeof(u_char), 0, &ipprotos); /* * Keep /etc/networks and /etc/protocols open for speedier queries. * Note: ip_host.c and ip_service.c make equivalent calls for their * respective /etc files. */ setnetent(1); setprotoent(1); return 1; } static int etherupdate; extern int _iphostbyname; static int ip_setopt(int id, char *val) { switch ((enum ip_propt) id) { case IP_PROPT_ETHERUPDATE: etherupdate = (*val != '0'); break; case IP_PROPT_HOSTBYNAME: _iphostbyname = (*val != '0'); break; case IP_PROPT_HOSTRESORDER: #ifdef __sgi sethostresorder(val); #endif break; default: return 0; } return 1; } /* ARGSUSED */ static void ip_embed(Protocol *pr, long prototype, long qualifier) { u_char p; p = prototype; in_enter(ipprotos, &p, pr); } Expr * ip_resolve(char *name, int len, Snooper *sn) { struct in_addr addr; u_long val; Expr *ex; if (ip_hostaddr(name, IP_HOST, &addr)) val = addr.s_addr; else { int cmd; struct sockaddr_in sin; if (len == 9 && !strcmp(name, "broadcast")) cmd = SIOCGIFBRDADDR; else if (len == 7 && !strcmp(name, "netmask")) cmd = SIOCGIFNETMASK; else { struct netent *np; np = getnetbyname(name); if (np == 0 || np->n_addrtype != AF_INET) return 0; cmd = 0; val = np->n_net; } if (cmd) { sin.sin_family = AF_INET; if (!sn_getaddr(sn, cmd, (struct sockaddr *) &sin)) return 0; val = ntohl(sin.sin_addr.s_addr); } } ex = expr(EXOP_NUMBER, EX_NULLARY, name); ex->ex_val = val; return ex; } static ExprType ip_compile(ProtoField *pf, Expr *mex, Expr *tex, ProtoCompiler *pc) { long mask, match; if (IPF_ISOPTION(pf)) return ET_COMPLEX; if (!pc_intmask(pc, mex, &mask)) return ET_ERROR; switch (tex->ex_op) { case EXOP_PROTOCOL: if (IPFID(pf) != P) { pc_badop(pc, tex); return ET_ERROR; } match = tex->ex_prsym->sym_prototype; break; case EXOP_NUMBER: match = tex->ex_val; break; default: pc_badop(pc, tex); return ET_ERROR; } if (!pc_intfield(pc, pf, mask, match, IP_MINHDRLEN)) return ET_COMPLEX; pc_stop(pc); return ET_SIMPLE; } /* * Fix ds so that we don't fetch or decode Ethernet minimum packet padding. */ static void ip_trim_ether_padding(int iplen, DataStream *ds) { int ipsize, padding; if (iplen < IP_MINHDRLEN) return; ipsize = ds->ds_size - sizeof(struct etherhdr); padding = MIN(ipsize, ETHERMIN) - iplen; if (padding > 0) { ds->ds_count -= padding; ds->ds_size -= padding; } } static int ip_match(Expr *pex, DataStream *ds, ProtoStack *ps, Expr *rex) { struct ip *ip; int hdrlen, optlen, matched; struct ipframe ipf; Entry **ep, *ent; Expr *mex; ip = (struct ip *) ds_inline(ds, sizeof *ip, IP_HDRGRAIN); if (ip == 0 || ip->ip_p != pex->ex_prsym->sym_prototype) return 0; /* * Trim minimum data link packet padding. */ if (ps->ps_top && ps->ps_top->psf_proto->pr_id == PRID_ETHER) ip_trim_ether_padding((u_short)ip->ip_len, ds); /* * Initialize an IP protocol stack frame. */ ipf.ipf_prototype = ip->ip_p; hdrlen = IP_HDRLEN(ip->ip_hl); ipf.ipf_hdrlen = hdrlen; ipf.ipf_len = ntohs((u_short)ip->ip_len); ipf.ipf_fragoffset = (ntohs(ip->ip_off) & ~(IP_DF|IP_MF)) << 3; ipf.ipf_morefrags = (ntohs(ip->ip_off) & IP_MF) != 0; ipf.ipf_src.s_addr = ntohl(ip->ip_src.s_addr); ipf.ipf_dst.s_addr = ipf.ipf_rdst.s_addr = ntohl(ip->ip_dst.s_addr); /* * Skip over any IP options, but save the last hop in a source route * in ipf.ipf_rdst. */ optlen = hdrlen - IP_MINHDRLEN; if (optlen > 0) { struct ip_route *ipr; char optbuf[IP_MAXOPTLEN]; ipr = (struct ip_route *) optbuf; while (optlen > 0) { if (!ds_ip_opt(ds, &ipr->ipr_hdr) && ipr->ipr_len == 0) break; if (ipr->ipr_opt == IPOPT_EOL) break; if ((ipr->ipr_opt == IPOPT_LSRR || ipr->ipr_opt == IPOPT_SSRR) && ipr->ipr_off < ipr->ipr_len) { ipf.ipf_rdst = ipr->ipr_addr[ipr->ipr_cnt-1]; break; } optlen -= ipr->ipr_len; } } /* * If this packet is a fragment, check whether we already matched * the first fragment of an IP datagram. If we matched, the cache * holds a copy of the result expression. */ if (ipf.ipf_fragoffset != 0) { mex = c_match(ipfragmatches, &ipf.ipf_fragkey); if (mex) *rex = *mex; return mex != 0; } /* * Evaluate the rest of the path expression. If there are more * fragments, remember whether this one matched or not, and if so, * save the result expression. */ PS_PUSH(ps, &ipf.ipf_frame, &ip_proto); matched = ex_match(pex, ds, ps, rex); PS_POP(ps); if (ipf.ipf_morefrags) { if (matched) mex = savematchresult(rex); else mex = 0; c_enter(ipfragmatches, &ipf.ipf_fragkey, mex); } return matched; } int ip_fetchopt(ProtoField *, int, DataStream *, Expr *); static int ip_fetch(ProtoField *pf, DataStream *ds, ProtoStack *ps, Expr *rex) { struct ip *ip; int skip; ip = (struct ip *) ds_inline(ds, sizeof *ip, IP_HDRGRAIN); if (ip) { skip = ip->ip_hl; switch (IPFID(pf)) { case V: rex->ex_val = ip->ip_v; break; case HL: rex->ex_val = skip; break; case TOS: rex->ex_val = ip->ip_tos; break; case LEN: rex->ex_val = ntohs((u_short)ip->ip_len); break; case ID: rex->ex_val = ntohs(ip->ip_id); break; case OFF: rex->ex_val = ntohs(ip->ip_off); break; case TTL: rex->ex_val = ip->ip_ttl; break; case P: rex->ex_val = ip->ip_p; break; case SUM: rex->ex_val = ntohs(ip->ip_sum); break; case SRC: rex->ex_val = ntohl(ip->ip_src.s_addr); break; case DST: rex->ex_val = ntohl(ip->ip_dst.s_addr); break; } rex->ex_op = EXOP_NUMBER; } else { if (!IPF_ISOPTION(pf) && !ds_field(ds, pf, IP_MINHDRLEN, rex)) { return 0; } return 1; } /* * Trim data link padding so our caller doesn't try to decode it. */ if (ps->ps_top && ps->ps_top->psf_proto->pr_id == PRID_ETHER) ip_trim_ether_padding((u_short)ip->ip_len, ds); skip = IP_HDRLEN(skip) - IP_MINHDRLEN; if (skip > 0) { if (IPF_ISOPTION(pf)) return ip_fetchopt(pf, skip, ds, rex); (void) ds_seek(ds, skip, DS_RELATIVE); } return 1; } static int ip_fetchopt(ProtoField *pf, int limit, DataStream *ds, Expr *rex) { char optbuf[IP_MAXOPTLEN]; struct ip_opt *ipo; limit += DS_TELL(ds); ipo = (struct ip_opt *) optbuf; if (!ds_ip_opt(ds, ipo)) return 0; switch (IPFID(pf)) { case OPT: rex->ex_val = ipo->ipo_opt; break; case OPTLEN: rex->ex_val = ipo->ipo_len; break; case RTOFF: case RTHOP: { struct ip_route *ipr; if (ipo->ipo_opt != IPOPT_RR && ipo->ipo_opt != IPOPT_LSRR && ipo->ipo_opt != IPOPT_SSRR) { return 0; } ipr = (struct ip_route *) ipo; if (ipr->ipr_off <= IPOPT_MINOFF || limit < ipr->ipr_off) return 0; if (IPFID(pf) == RTOFF) rex->ex_val = ipr->ipr_off; else { int index; /* index of route addr to fetch */ index = (ipr->ipr_off - IPOPT_MINOFF) / sizeof ipr->ipr_addr[0]; if (ipo->ipo_opt == IPOPT_RR) --index; rex->ex_val = ntohl(ipr->ipr_addr[index].s_addr); } break; } default: { struct ip_timestamp *ipt; #define lastset(ipt,vecname) \ ipt->ipt_timestamp.vecname[(ipt->ipt_ptr - (IPOPT_MINOFF+1) \ - sizeof ipt->ipt_timestamp.vecname[0]) \ / sizeof ipt->ipt_timestamp.vecname[0]] if (ipo->ipo_opt != IPOPT_TS) return 0; ipt = (struct ip_timestamp *) ipo; switch (IPFID(pf)) { case TSPTR: rex->ex_val = ipt->ipt_ptr; break; case TSOFLW: rex->ex_val = ipt->ipt_oflw; break; case TSFLG: rex->ex_val = ipt->ipt_flg; break; case TSTIME: case TSADDR: if (ipt->ipt_ptr <= IPOPT_MINOFF + 1 || limit < ipt->ipt_ptr) { return 0; } switch (ipt->ipt_flg) { case IPOPT_TS_TSONLY: if (IPFID(pf) != TSTIME) return 0; rex->ex_val = lastset(ipt, ipt_time); break; case IPOPT_TS_TSANDADDR: case IPOPT_TS_PRESPEC: { struct ipt_ta *ta; ta = &lastset(ipt, ipt_ta); rex->ex_val = (IPFID(pf) == TSADDR) ? ta->ipt_addr.s_addr : ta->ipt_time; break; } default: return 0; } rex->ex_val = ntohl(rex->ex_val); break; } rex->ex_op = EXOP_NUMBER; #undef lastset } } (void) ds_seek(ds, limit, DS_ABSOLUTE); return 1; } /* * For IP over Ethernet, update ether hostname info based on ip. */ static void ip_update_ether_hosts(struct ip *ip, ProtoStack *ps, char *srcname, char *dstname) { struct sockaddr *sa; int localnet; struct etherframe *ef; sa = &ps->ps_snoop->sn_ifaddr; if (sa->sa_family != AF_INET) { sa->sa_family = AF_INET; if (!sn_getaddr(ps->ps_snoop, SIOCGIFADDR, sa)) bzero(sa, sizeof *sa); } localnet = inet_netof(((struct sockaddr_in *)sa)->sin_addr); ef = PS_TOP(ps, struct etherframe); if (inet_netof(ip->ip_src.s_addr) == localnet) (void) ether_addhost(srcname, &ef->ef_src); if (inet_netof(ip->ip_dst.s_addr) == localnet && bcmp(&ef->ef_dst, ðerbroadcastaddr, sizeof ef->ef_dst)) { (void) ether_addhost(dstname, &ef->ef_dst); } } void ip_decodeopt(struct ip_opt *, struct ipframe *, PacketView *); void ip_decodeopts(int, DataStream *, struct ipframe *, PacketView *); static void ip_decode(DataStream *ds, ProtoStack *ps, PacketView *pv) { struct ip *ip, hdr; u_long idealsum, src, dst; int hdrlen, optlen; Protocol *pr; char *srcname, *dstname; struct ipframe ipf; Entry **ep, *ent; ProtoDecodeState *pds; ip = (struct ip *) ds_inline(ds, sizeof *ip, IP_HDRGRAIN); if (ip == 0) { idealsum = 0; ip = &hdr; ds_ip(ds, ip); /* * Keep src and dst in net order for convenience. */ ip->ip_src.s_addr = htonl(ip->ip_src.s_addr); ip->ip_dst.s_addr = htonl(ip->ip_dst.s_addr); } else { u_short realsum; realsum = ip->ip_sum; hdrlen = IP_HDRLEN(ip->ip_hl); optlen = hdrlen - IP_MINHDRLEN; if (optlen > ds->ds_count) idealsum = 0; else { u_short *ips; /* * Optimized in-line expansion of ip_checksum. */ ip->ip_sum = 0; ips = (u_short *) ip; idealsum = ips[0] + ips[1] + ips[2] + ips[3] + ips[4] + ips[6] + ips[7] + ips[8] + ips[9]; if (optlen > 0) { optlen /= sizeof *ips; ips += sizeof *ip / sizeof *ips; while (--optlen >= 0) idealsum += *ips++; } idealsum = IP_FOLD_CHECKSUM_CARRY(idealsum); } ip->ip_len = ntohs((u_short)ip->ip_len); ip->ip_id = ntohs(ip->ip_id); ip->ip_off = ntohs(ip->ip_off); ip->ip_sum = ntohs(realsum); } pv_showfield(pv, &IPFIELD(V), &ip, "%-2d", ip->ip_v); pv_showfield(pv, &IPFIELD(HL), &ip, "%-2d", ip->ip_hl); pv_showfield(pv, &IPFIELD(TOS), &ip->ip_tos, "%#-4x", ip->ip_tos); pv_showfield(pv, &IPFIELD(LEN), &ip->ip_len, "%-5u", (u_short) ip->ip_len); pv_showfield(pv, &IPFIELD(ID), &ip->ip_id, "%-5u", ip->ip_id); pv_showfield(pv, &IPFIELD(OFF), &ip->ip_off, "%d%s%s", ip->ip_off & ~(IP_DF|IP_MF), (ip->ip_off & IP_DF) ? " DF" : "", (ip->ip_off & IP_MF) ? " MF" : ""); if (pv->pv_level >= PV_VERBOSE) pv_break(pv); pv_showfield(pv, &IPFIELD(TTL), &ip->ip_ttl, "%-3d", ip->ip_ttl); pr = in_match(ipprotos, &ip->ip_p); if (pr) { pv_showfield(pv, &IPFIELD(P), &ip->ip_p, "%-6s", pr->pr_name); } else { pv_showfield(pv, &IPFIELD(P), &ip->ip_p, "@t%-6s", ip_protoname(ip->ip_p)); } if (idealsum && ip->ip_sum != idealsum) { pv_showfield(pv, &IPFIELD(SUM), &ip->ip_sum, "%#x [!= %#x]", ip->ip_sum, idealsum); } else { pv_showfield(pv, &IPFIELD(SUM), &ip->ip_sum, "%#-6x", ip->ip_sum); } pv_break(pv); src = ntohl(ip->ip_src.s_addr); srcname = ip_hostname(ip->ip_src, IP_NET); pv_showfield(pv, &IPFIELD(SRC), &src, "%-25.25s", srcname); dst = ntohl(ip->ip_dst.s_addr); dstname = ip_hostname(ip->ip_dst, IP_NET); pv_showfield(pv, &IPFIELD(DST), &dst, "%-25.25s", dstname); /* * If this packet is local IP traffic over Ethernet, update the * Ethernet host name/address mappings with IP hostnames. Also * trim Ethernet padding. */ if (ps->ps_top) { int linkprid; linkprid = ps->ps_top->psf_proto->pr_id; if (linkprid == PRID_ETHER) { if (etherupdate) ip_update_ether_hosts(ip, ps, srcname, dstname); ip_trim_ether_padding((u_short)ip->ip_len, ds); } } /* * Initialize an ipframe for higher layers. */ ipf.ipf_prototype = ip->ip_p; ipf.ipf_hdrlen = hdrlen; ipf.ipf_len = (u_short)ip->ip_len; ipf.ipf_id = ip->ip_id; ipf.ipf_fragoffset = (ip->ip_off & ~(IP_DF|IP_MF)) << 3; ipf.ipf_morefrags = (ip->ip_off & IP_MF) != 0; ipf.ipf_src.s_addr = src; ipf.ipf_dst.s_addr = ipf.ipf_rdst.s_addr = dst; /* * Decode IP options, if any. */ hdrlen = IP_HDRLEN(ip->ip_hl); optlen = hdrlen - IP_MINHDRLEN; if (optlen > 0) ip_decodeopts(optlen, ds, &ipf, pv); /* * If this packet contains a non-initial IP fragment, try to find * the IP datagram's protocol decode state. Higher layer protocols * will set ps->ps_state to point at dynamically allocated decode * state when they see an initial fragment. */ if (ipf.ipf_fragoffset != 0) { pds = c_match(ipfragdecodes, &ipf.ipf_fragkey); if (pds) { ps->ps_state = pds; pr = pds->pds_proto; } else pr = 0; pv_decodeframe(pv, pr, ds, ps); return; } /* * Decode the first or only fragment of an IP datagram. If a higher * layer set some protocol decode state and there are more fragments, * associate the state with ipf_fragkey. */ PS_PUSH(ps, &ipf.ipf_frame, &ip_proto); pv_decodeframe(pv, pr, ds, ps); PS_POP(ps); pds = ps->ps_state; if (pds && ipf.ipf_morefrags) c_enter(ipfragdecodes, &ipf.ipf_fragkey, pds); } static void ip_decodeopts(int optlen, DataStream *ds, struct ipframe *ipf, PacketView *pv) { struct ip_opt *ipo; char optbuf[IP_MAXOPTLEN]; int off, len; ipo = (struct ip_opt *) optbuf; while (optlen > 0) { if (!ds_ip_opt(ds, ipo) && ipo->ipo_len == 0) break; optlen -= ipo->ipo_len; off = pv->pv_off + ipo->ipo_len; ip_decodeopt(ipo, ipf, pv); len = off - pv->pv_off; if (len > 0) { pv_decodehex(pv, ds, pv->pv_off, len); pv->pv_off = off; } } } static void ip_decodeopt(struct ip_opt *ipo, struct ipframe *ipf, PacketView *pv) { int len; pv_break(pv); pv_showfield(pv, &IPFIELD(OPT), &ipo->ipo_opt, "%-11s", en_name(&ipopt, ipo->ipo_opt)); switch (ipo->ipo_opt) { case IPOPT_EOL: case IPOPT_NOP: break; case IPOPT_RR: case IPOPT_LSRR: case IPOPT_SSRR: { struct ip_route *ipr; struct in_addr *hop; ipr = (struct ip_route *) ipo; pv_showfield(pv, &IPFIELD(OPTLEN), &ipr->ipr_len, "%-3u", ipr->ipr_len); pv_showfield(pv, &IPFIELD(RTOFF), &ipr->ipr_off, "%-3u", ipr->ipr_off); pv_break(pv); len = ipr->ipr_cnt; if (ipr->ipr_opt != IPOPT_RR && ipr->ipr_off < ipr->ipr_len) ipf->ipf_rdst = ipr->ipr_addr[len-1]; for (hop = ipr->ipr_addr; --len >= 0; hop++) { pv_showfield(pv, &IPFIELD(RTHOP), hop, "%-25.25s", ip_hostname(*hop, IP_HOST)); } break; } case IPOPT_TS: { struct ip_timestamp *ipt; ipt = (struct ip_timestamp *) ipo; pv_showfield(pv, &IPFIELD(OPTLEN), &ipt->ipt_len, "%-3u", ipt->ipt_len); pv_showfield(pv, &IPFIELD(TSPTR), &ipt->ipt_ptr, "%-3u", ipt->ipt_ptr); pv_showfield(pv, &IPFIELD(TSOFLW), &ipt->ipt_ptr + 1, "%u", ipt->ipt_oflw); pv_showfield(pv, &IPFIELD(TSFLG), &ipt->ipt_ptr + 1, "%u", ipt->ipt_flg); len = ipt->ipt_ptr; if (len > IP_MAXOPTLEN) len = IP_MAXOPTLEN; len -= IPOPT_MINOFF + 1; pv_break(pv); switch (ipt->ipt_flg) { case IPOPT_TS_TSONLY: { n_long *np; len /= sizeof *np; for (np = ipt->ipt_timestamp.ipt_time; --len >= 0; np++) { pv_showfield(pv, &IPFIELD(TSTIME), np, "%-12.12s", ip_timestamp(*np)); } break; } case IPOPT_TS_TSANDADDR: case IPOPT_TS_PRESPEC: { struct ipt_ta *ta; len /= sizeof *ta; for (ta = ipt->ipt_timestamp.ipt_ta; --len >= 0; ta++) { pv_showfield(pv, &IPFIELD(TSADDR), &ta->ipt_addr, "%-25.25s", ip_hostname(ta->ipt_addr,IP_HOST)); pv_showfield(pv, &IPFIELD(TSTIME), &ta->ipt_time, "%-12.12s", ip_timestamp(ta->ipt_time)); } } } break; } default: if (pv->pv_level < PV_VERBOSE) break; len = MIN(ipo->ipo_len, IP_MAXOPTLEN) - 1; if (len > 0) pv_showhex(pv, (u_char*)&ipo->ipo_len, pv->pv_off, len); } }