static void process(int fd) { struct sockaddr from; socklen_t fromlen; int cc; union { char buf[MAXPACKETSIZE+1]; struct rip rip; } inbuf; for (;;) { fromlen = sizeof(from); cc = recvfrom(fd, &inbuf, sizeof(inbuf), 0, &from, &fromlen); if (cc <= 0) { if (cc < 0 && errno != EWOULDBLOCK) perror("recvfrom"); break; } if (fromlen != sizeof (struct sockaddr_in)) { break; } rip_input(&from, &inbuf.rip, cc); } }
/* * De-encapsulate a packet and feed it back through ip input (this * routine is called whenever IP gets a packet with proto type * IPPROTO_GRE and a local destination address). * This really is simple */ void gre_input(struct mbuf *m, int off) { int proto; proto = (mtod(m, struct ip *))->ip_p; m = gre_input2(m, off, proto); /* * If no matching tunnel that is up is found. We inject * the mbuf to raw ip socket to see if anyone picks it up. */ if (m != NULL) rip_input(m, off); }
void process(int fd, int pkt_type) { struct sockaddr from; int fromlen = sizeof (from), cc, omask; struct ipx *ipxdp = (struct ipx *)packet; cc = recvfrom(fd, packet, sizeof (packet), 0, &from, &fromlen); if (cc <= 0) { if (cc < 0 && errno != EINTR) syslog(LOG_ERR, "recvfrom: %m"); return; } if (tracepackets > 1 && ftrace) { fprintf(ftrace,"rcv %d bytes on %s ", cc, ipxdp_ntoa(&ipxdp->ipx_dna)); fprintf(ftrace," from %s\n", ipxdp_ntoa(&ipxdp->ipx_sna)); } if (noteremoterequests && !ipx_neteqnn(ipxdp->ipx_sna.x_net, ipx_zeronet) && !ipx_neteq(ipxdp->ipx_sna, ipxdp->ipx_dna)) { syslog(LOG_ERR, "net of interface (%s) != net on ether (%s)!\n", ipxdp_nettoa(ipxdp->ipx_dna.x_net), ipxdp_nettoa(ipxdp->ipx_sna.x_net)); } /* We get the IPX header in front of the RIF packet*/ cc -= sizeof (struct ipx); #define mask(s) (1<<((s)-1)) omask = sigblock(mask(SIGALRM)); switch(pkt_type) { case SAP_PKT: sap_input(&from, cc); break; case RIP_PKT: rip_input(&from, cc); break; } sigsetmask(omask); }
/* * De-encapsulate a packet and feed it back through ip input (this * routine is called whenever IP gets a packet with proto type * IPPROTO_GRE and a local destination address). * This really is simple */ int gre_input(struct mbuf **mp, int *offp, int proto) { struct mbuf *m; int ret, off; off = *offp; m = *mp; *mp = NULL; proto = (mtod(m, struct ip *))->ip_p; ret = gre_input2(m, off, proto); /* * ret == 0 : packet not processed, meaning that * no matching tunnel that is up is found. * we inject it to raw ip socket to see if anyone picks it up. */ if (ret == 0) { *mp = m; rip_input(mp, offp, proto); } return(IPPROTO_DONE); }
void igmp_input(struct mbuf *m, int iphlen) { register struct igmp *igmp; register struct ip *ip; register int igmplen; register struct ifnet *ifp = m->m_pkthdr.rcvif; register int minlen; register struct in_multi *inm; register struct in_ifaddr *ia; struct in_multistep step; struct router_info *rti; int timer; /** timer value in the igmp query header **/ ++igmpstat.igps_rcv_total; ip = mtod(m, struct ip *); igmplen = ip->ip_len; /* * Validate lengths */ if (igmplen < IGMP_MINLEN) { ++igmpstat.igps_rcv_tooshort; m_freem(m); return; } minlen = iphlen + IGMP_MINLEN; if ((m->m_flags & M_EXT || m->m_len < minlen) && (m = m_pullup(m, minlen)) == 0) { ++igmpstat.igps_rcv_tooshort; return; } /* * Validate checksum */ m->m_data += iphlen; m->m_len -= iphlen; igmp = mtod(m, struct igmp *); if (in_cksum(m, igmplen)) { ++igmpstat.igps_rcv_badsum; m_freem(m); return; } m->m_data -= iphlen; m->m_len += iphlen; ip = mtod(m, struct ip *); timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; rti = find_rti(ifp); /* * In the IGMPv2 specification, there are 3 states and a flag. * * In Non-Member state, we simply don't have a membership record. * In Delaying Member state, our timer is running (inm->inm_timer) * In Idle Member state, our timer is not running (inm->inm_timer==0) * * The flag is inm->inm_state, it is set to IGMP_OTHERMEMBER if * we have heard a report from another member, or IGMP_IREPORTEDLAST * if I sent the last report. */ switch (igmp->igmp_type) { case IGMP_MEMBERSHIP_QUERY: ++igmpstat.igps_rcv_queries; if (ifp->if_flags & IFF_LOOPBACK) break; if (igmp->igmp_code == 0) { /* * Old router. Remember that the querier on this * interface is old, and set the timer to the * value in RFC 1112. */ rti->rti_type = IGMP_V1_ROUTER; rti->rti_time = 0; timer = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ; if (ip->ip_dst.s_addr != igmp_all_hosts_group || igmp->igmp_group.s_addr != 0) { ++igmpstat.igps_rcv_badqueries; m_freem(m); return; } } else { /* * New router. Simply do the new validity check. */ if (igmp->igmp_group.s_addr != 0 && !IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { ++igmpstat.igps_rcv_badqueries; m_freem(m); return; } } /* * - Start the timers in all of our membership records * that the query applies to for the interface on * which the query arrived excl. those that belong * to the "all-hosts" group (224.0.0.1). * - Restart any timer that is already running but has * a value longer than the requested timeout. * - Use the value specified in the query message as * the maximum timeout. */ IN_FIRST_MULTI(step, inm); while (inm != NULL) { if (inm->inm_ifp == ifp && inm->inm_addr.s_addr != igmp_all_hosts_group && (igmp->igmp_group.s_addr == 0 || igmp->igmp_group.s_addr == inm->inm_addr.s_addr)) { if (inm->inm_timer == 0 || inm->inm_timer > timer) { inm->inm_timer = IGMP_RANDOM_DELAY(timer); igmp_timers_are_running = 1; } } IN_NEXT_MULTI(step, inm); } break; case IGMP_V1_MEMBERSHIP_REPORT: case IGMP_V2_MEMBERSHIP_REPORT: /* * For fast leave to work, we have to know that we are the * last person to send a report for this group. Reports * can potentially get looped back if we are a multicast * router, so discard reports sourced by me. */ IFP_TO_IA(ifp, ia); if (ia && ip->ip_src.s_addr == IA_SIN(ia)->sin_addr.s_addr) break; ++igmpstat.igps_rcv_reports; if (ifp->if_flags & IFF_LOOPBACK) break; if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { ++igmpstat.igps_rcv_badreports; m_freem(m); return; } /* * KLUDGE: if the IP source address of the report has an * unspecified (i.e., zero) subnet number, as is allowed for * a booting host, replace it with the correct subnet number * so that a process-level multicast routing demon can * determine which subnet it arrived from. This is necessary * to compensate for the lack of any way for a process to * determine the arrival interface of an incoming packet. */ if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); /* * If we belong to the group being reported, stop * our timer for that group. */ IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); if (inm != NULL) { inm->inm_timer = 0; ++igmpstat.igps_rcv_ourreports; inm->inm_state = IGMP_OTHERMEMBER; } break; } /* * Pass all valid IGMP packets up to any process(es) listening * on a raw IGMP socket. */ rip_input(m, iphlen); }
/* * ipsec_common_input() gets called when we receive an IPsec-protected packet * in IPv4 or IPv6. All it does is find the right TDB and call the appropriate * transform. The callback takes care of further processing (like ingress * filtering). */ int ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto, int udpencap) { #define IPSEC_ISTAT(x,y,z) (sproto == IPPROTO_ESP ? (x)++ : \ sproto == IPPROTO_AH ? (y)++ : (z)++) union sockaddr_union dst_address; struct timeval tv; struct tdb *tdbp; struct ifnet *encif; u_int32_t spi; u_int16_t cpi; int s, error; IPSEC_ISTAT(espstat.esps_input, ahstat.ahs_input, ipcompstat.ipcomps_input); if (m == 0) { DPRINTF(("ipsec_common_input(): NULL packet received\n")); IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops, ipcompstat.ipcomps_hdrops); return EINVAL; } if ((sproto == IPPROTO_ESP && !esp_enable) || (sproto == IPPROTO_AH && !ah_enable) || #if NPF > 0 (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) || #endif (sproto == IPPROTO_IPCOMP && !ipcomp_enable)) { switch (af) { #ifdef INET case AF_INET: rip_input(m, skip, sproto); break; #endif /* INET */ #ifdef INET6 case AF_INET6: rip6_input(&m, &skip, sproto); break; #endif /* INET6 */ default: DPRINTF(("ipsec_common_input(): unsupported protocol " "family %d\n", af)); m_freem(m); IPSEC_ISTAT(espstat.esps_nopf, ahstat.ahs_nopf, ipcompstat.ipcomps_nopf); return EPFNOSUPPORT; } return 0; } if ((sproto == IPPROTO_IPCOMP) && (m->m_flags & M_COMP)) { m_freem(m); ipcompstat.ipcomps_pdrops++; DPRINTF(("ipsec_common_input(): repeated decompression\n")); return EINVAL; } if (m->m_pkthdr.len - skip < 2 * sizeof(u_int32_t)) { m_freem(m); IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops, ipcompstat.ipcomps_hdrops); DPRINTF(("ipsec_common_input(): packet too small\n")); return EINVAL; } /* Retrieve the SPI from the relevant IPsec header */ if (sproto == IPPROTO_ESP) m_copydata(m, skip, sizeof(u_int32_t), (caddr_t) &spi); else if (sproto == IPPROTO_AH) m_copydata(m, skip + sizeof(u_int32_t), sizeof(u_int32_t), (caddr_t) &spi); else if (sproto == IPPROTO_IPCOMP) { m_copydata(m, skip + sizeof(u_int16_t), sizeof(u_int16_t), (caddr_t) &cpi); spi = ntohl(htons(cpi)); } /* * Find tunnel control block and (indirectly) call the appropriate * kernel crypto routine. The resulting mbuf chain is a valid * IP packet ready to go through input processing. */ memset(&dst_address, 0, sizeof(dst_address)); dst_address.sa.sa_family = af; switch (af) { #ifdef INET case AF_INET: dst_address.sin.sin_len = sizeof(struct sockaddr_in); m_copydata(m, offsetof(struct ip, ip_dst), sizeof(struct in_addr), (caddr_t) &(dst_address.sin.sin_addr)); break; #endif /* INET */ #ifdef INET6 case AF_INET6: dst_address.sin6.sin6_len = sizeof(struct sockaddr_in6); m_copydata(m, offsetof(struct ip6_hdr, ip6_dst), sizeof(struct in6_addr), (caddr_t) &(dst_address.sin6.sin6_addr)); in6_recoverscope(&dst_address.sin6, &dst_address.sin6.sin6_addr, NULL); break; #endif /* INET6 */ default: DPRINTF(("ipsec_common_input(): unsupported protocol " "family %d\n", af)); m_freem(m); IPSEC_ISTAT(espstat.esps_nopf, ahstat.ahs_nopf, ipcompstat.ipcomps_nopf); return EPFNOSUPPORT; } s = splsoftnet(); tdbp = gettdb(rtable_l2(m->m_pkthdr.ph_rtableid), spi, &dst_address, sproto); if (tdbp == NULL) { splx(s); DPRINTF(("ipsec_common_input(): could not find SA for " "packet to %s, spi %08x\n", ipsp_address(dst_address), ntohl(spi))); m_freem(m); IPSEC_ISTAT(espstat.esps_notdb, ahstat.ahs_notdb, ipcompstat.ipcomps_notdb); return ENOENT; } if (tdbp->tdb_flags & TDBF_INVALID) { splx(s); DPRINTF(("ipsec_common_input(): attempted to use invalid SA %s/%08x/%u\n", ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto)); m_freem(m); IPSEC_ISTAT(espstat.esps_invalid, ahstat.ahs_invalid, ipcompstat.ipcomps_invalid); return EINVAL; } if (udpencap && !(tdbp->tdb_flags & TDBF_UDPENCAP)) { splx(s); DPRINTF(("ipsec_common_input(): attempted to use non-udpencap SA %s/%08x/%u\n", ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto)); m_freem(m); espstat.esps_udpinval++; return EINVAL; } if (tdbp->tdb_xform == NULL) { splx(s); DPRINTF(("ipsec_common_input(): attempted to use uninitialized SA %s/%08x/%u\n", ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto)); m_freem(m); IPSEC_ISTAT(espstat.esps_noxform, ahstat.ahs_noxform, ipcompstat.ipcomps_noxform); return ENXIO; } if (sproto != IPPROTO_IPCOMP) { if ((encif = enc_getif(tdbp->tdb_rdomain, tdbp->tdb_tap)) == NULL) { splx(s); DPRINTF(("ipsec_common_input(): " "no enc%u interface for SA %s/%08x/%u\n", tdbp->tdb_tap, ipsp_address(dst_address), ntohl(spi), tdbp->tdb_sproto)); m_freem(m); IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops, ipcompstat.ipcomps_pdrops); return EACCES; } /* XXX This conflicts with the scoped nature of IPv6 */ m->m_pkthdr.rcvif = encif; } /* Register first use, setup expiration timer. */ if (tdbp->tdb_first_use == 0) { tdbp->tdb_first_use = time_second; tv.tv_usec = 0; tv.tv_sec = tdbp->tdb_exp_first_use + tdbp->tdb_first_use; if (tdbp->tdb_flags & TDBF_FIRSTUSE) timeout_add(&tdbp->tdb_first_tmo, hzto(&tv)); tv.tv_sec = tdbp->tdb_first_use + tdbp->tdb_soft_first_use; if (tdbp->tdb_flags & TDBF_SOFT_FIRSTUSE) timeout_add(&tdbp->tdb_sfirst_tmo, hzto(&tv)); } /* * Call appropriate transform and return -- callback takes care of * everything else. */ error = (*(tdbp->tdb_xform->xf_input))(m, tdbp, skip, protoff); splx(s); return error; }
/* Query all of the listed hosts */ static void query_loop(char *argv[], int argc, int soc) { #define NA0 (OMSG.rip_auths[0]) #define NA2 (OMSG.rip_auths[2]) struct seen { struct seen *next; struct in_addr addr; } *seen, *sp; int answered = 0; int cc; fd_set bits; struct timeval now, delay; struct sockaddr_in from; MD5_CTX md5_ctx; struct msghdr msg; uint_t ifindex; struct iovec iov; uint8_t ancillary_data[CONTROL_BUFSIZE]; OMSG.rip_cmd = (pflag) ? RIPCMD_POLL : RIPCMD_REQUEST; if (ripv2) { OMSG.rip_vers = RIPv2; if (auth_type == RIP_AUTH_PW) { OMSG.rip_nets[1] = OMSG.rip_nets[0]; NA0.a_family = RIP_AF_AUTH; NA0.a_type = RIP_AUTH_PW; (void) memcpy(NA0.au.au_pw, passwd, RIP_AUTH_PW_LEN); omsg_len += sizeof (OMSG.rip_nets[0]); } else if (auth_type == RIP_AUTH_MD5) { OMSG.rip_nets[1] = OMSG.rip_nets[0]; NA0.a_family = RIP_AF_AUTH; NA0.a_type = RIP_AUTH_MD5; NA0.au.a_md5.md5_keyid = (int8_t)keyid; NA0.au.a_md5.md5_auth_len = RIP_AUTH_MD5_LEN; NA0.au.a_md5.md5_seqno = 0; cc = (char *)&NA2-(char *)&OMSG; NA0.au.a_md5.md5_pkt_len = htons(cc); NA2.a_family = RIP_AF_AUTH; NA2.a_type = RIP_AUTH_TRAILER; MD5Init(&md5_ctx); MD5Update(&md5_ctx, (uchar_t *)&OMSG, cc+4); MD5Update(&md5_ctx, (uchar_t *)passwd, RIP_AUTH_MD5_LEN); MD5Final(NA2.au.au_pw, &md5_ctx); omsg_len += 2*sizeof (OMSG.rip_nets[0]); } } else { OMSG.rip_vers = RIPv1; OMSG.rip_nets[0].n_mask = 0; } /* ask the first (valid) host */ seen = NULL; while (0 > out(*argv++, soc)) { if (*argv == NULL) exit(EXIT_FAILURE); answered++; } iov.iov_base = &imsg_buf; iov.iov_len = sizeof (imsg_buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = &from; msg.msg_control = &ancillary_data; (void) FD_ZERO(&bits); FD_SET(soc, &bits); for (;;) { delay.tv_sec = 0; delay.tv_usec = STIME; cc = select(soc+1, &bits, 0, 0, &delay); if (cc > 0) { msg.msg_namelen = sizeof (from); msg.msg_controllen = sizeof (ancillary_data); cc = recvmsg(soc, &msg, 0); if (cc < 0) { perror("rtquery: recvmsg"); exit(EXIT_FAILURE); } /* avoid looping on high traffic */ if (answered > argc + 200) break; /* * count the distinct responding hosts. * You cannot match responding hosts with * addresses to which queries were transmitted, * because a router might respond with a * different source address. */ for (sp = seen; sp != NULL; sp = sp->next) { if (sp->addr.s_addr == from.sin_addr.s_addr) break; } if (sp == NULL) { sp = malloc(sizeof (*sp)); if (sp != NULL) { sp->addr = from.sin_addr; sp->next = seen; seen = sp; } else { perror("rtquery: malloc"); } answered++; } ifindex = incoming_interface(&msg); rip_input(&from, cc, ifindex); continue; } if (cc < 0) { if (errno == EINTR) continue; perror("rtquery: select"); exit(EXIT_FAILURE); } /* * After a pause in responses, probe another host. * This reduces the intermingling of answers. */ while (*argv != NULL && 0 > out(*argv++, soc)) answered++; /* * continue until no more packets arrive * or we have heard from all hosts */ if (answered >= argc) break; /* or until we have waited a long time */ if (gettimeofday(&now, 0) < 0) { perror("rtquery: gettimeofday"); exit(EXIT_FAILURE); } if (sent.tv_sec + wtime <= now.tv_sec) break; } /* fail if there was no answer */ exit(answered >= argc ? EXIT_SUCCESS : EXIT_FAILURE); }
int main(int argc, char *argv[]) { int ch, cc, count=0, bits; struct sockaddr from; struct sigaction sigact; socklen_t fromlen = sizeof(from); int size = 32*1024; struct timeval shorttime; while ((ch = getopt(argc, argv, "n")) != EOF) switch (ch) { case 'n': nflag++; break; case '?': default: goto usage; } argv += optind; if (!*argv) { usage: printf("usage: ripquery [-n] hosts...\n"); exit(1); } s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); exit(2); } if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) perror("setsockopt SO_RCVBUF"); while (*argv) { query(*argv++); count++; } /* * Listen for returning packets; * may be more than one packet per host. */ bits = 1 << s; memset(&shorttime, 0, sizeof(shorttime)); shorttime.tv_usec = STIME; memset(&sigact, 0, sizeof(sigact)); sigact.sa_handler = timeout; /*sigact.sa_flags = 0;*/ /* no restart */ if (sigaction(SIGALRM, &sigact, (struct sigaction *)NULL) == -1) perror("sigaction"); alarm(WTIME); while ((count > 0 && !timedout) || select(20, (fd_set *)&bits, NULL, NULL, &shorttime) > 0) { cc = recvfrom(s, packet, sizeof (packet), 0, &from, &fromlen); if (cc <= 0) { if (cc < 0) { if (errno == EINTR) continue; perror("recvfrom"); (void) close(s); exit(1); } continue; } rip_input(&from, cc); count--; } exit (count > 0 ? count : 0); }
void encap4_input(struct mbuf *m, int off) { int proto; struct ip *ip; struct sockaddr_in s, d; const struct protosw *psw; struct encaptab *ep, *match; int prio, matchprio; #ifndef __APPLE__ va_start(ap, m); off = va_arg(ap, int); proto = va_arg(ap, int); va_end(ap); #endif /* Expect 32-bit aligned data pointer on strict-align platforms */ MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); ip = mtod(m, struct ip *); #ifdef __APPLE__ proto = ip->ip_p; #endif bzero(&s, sizeof(s)); s.sin_family = AF_INET; s.sin_len = sizeof(struct sockaddr_in); s.sin_addr = ip->ip_src; bzero(&d, sizeof(d)); d.sin_family = AF_INET; d.sin_len = sizeof(struct sockaddr_in); d.sin_addr = ip->ip_dst; match = NULL; matchprio = 0; for (ep = LIST_FIRST(&encaptab); ep; ep = LIST_NEXT(ep, chain)) { if (ep->af != AF_INET) continue; if (ep->proto >= 0 && ep->proto != proto) continue; if (ep->func) prio = (*ep->func)(m, off, proto, ep->arg); else { /* * it's inbound traffic, we need to match in reverse * order */ prio = mask_match(ep, (struct sockaddr *)&d, (struct sockaddr *)&s); } /* * We prioritize the matches by using bit length of the * matches. mask_match() and user-supplied matching function * should return the bit length of the matches (for example, * if both src/dst are matched for IPv4, 64 should be returned). * 0 or negative return value means "it did not match". * * The question is, since we have two "mask" portion, we * cannot really define total order between entries. * For example, which of these should be preferred? * mask_match() returns 48 (32 + 16) for both of them. * src=3ffe::/16, dst=3ffe:501::/32 * src=3ffe:501::/32, dst=3ffe::/16 * * We need to loop through all the possible candidates * to get the best match - the search takes O(n) for * n attachments (i.e. interfaces). */ if (prio <= 0) continue; if (prio > matchprio) { matchprio = prio; match = ep; } } if (match) { /* found a match, "match" has the best one */ psw = (const struct protosw *)match->psw; if (psw && psw->pr_input) { encap_fillarg(m, match); (*psw->pr_input)(m, off); } else m_freem(m); return; } /* last resort: inject to raw socket */ rip_input(m, off); }