static int udp_mcast_input(struct udp_mcast_arg *arg) { struct inpcb *inp = arg->inp; struct inpcb *last = arg->last; struct ip *ip = arg->ip; struct mbuf *m = arg->m; if (check_multicast_membership(ip, inp, m) < 0) return ERESTART; /* caller continue */ if (last != NULL) { struct mbuf *n; #ifdef IPSEC /* check AH/ESP integrity. */ if (ipsec4_in_reject_so(m, last->inp_socket)) ipsecstat.in_polvio++; /* do not inject data to pcb */ else #endif /*IPSEC*/ #ifdef FAST_IPSEC /* check AH/ESP integrity. */ if (ipsec4_in_reject(m, last)) ; else #endif /*FAST_IPSEC*/ if ((n = m_copypacket(m, M_NOWAIT)) != NULL) udp_append(last, ip, n, arg->iphlen + sizeof(struct udphdr), arg->udp_in); } arg->last = last = inp; /* * Don't look for additional matches if this one does * not have either the SO_REUSEPORT or SO_REUSEADDR * socket options set. This heuristic avoids searching * through all pcbs in the common case of a non-shared * port. It * assumes that an application will never * clear these options after setting them. */ if (!(last->inp_socket->so_options & (SO_REUSEPORT | SO_REUSEADDR))) return EJUSTRETURN; /* caller stop */ return 0; }
int udp_input(struct mbuf **mp, int *offp, int proto) { struct sockaddr_in udp_in = { sizeof udp_in, AF_INET }; int iphlen; struct ip *ip; struct udphdr *uh; struct inpcb *inp; struct mbuf *m; struct mbuf *opts = NULL; int len, off; struct ip save_ip; struct inpcbinfo *pcbinfo = &udbinfo[mycpuid]; off = *offp; m = *mp; *mp = NULL; iphlen = off; udp_stat.udps_ipackets++; /* * Strip IP options, if any; should skip this, * make available to user, and use on returned packets, * but we don't yet have a way to check the checksum * with options still present. */ if (iphlen > sizeof(struct ip)) { ip_stripoptions(m); iphlen = sizeof(struct ip); } /* * IP and UDP headers are together in first mbuf. * Already checked and pulled up in ip_demux(). */ KASSERT(m->m_len >= iphlen + sizeof(struct udphdr), ("UDP header not in one mbuf")); ip = mtod(m, struct ip *); uh = (struct udphdr *)((caddr_t)ip + iphlen); /* destination port of 0 is illegal, based on RFC768. */ if (uh->uh_dport == 0) goto bad; /* * Make mbuf data length reflect UDP length. * If not enough data to reflect UDP length, drop. */ len = ntohs((u_short)uh->uh_ulen); if (ip->ip_len != len) { if (len > ip->ip_len || len < sizeof(struct udphdr)) { udp_stat.udps_badlen++; goto bad; } m_adj(m, len - ip->ip_len); /* ip->ip_len = len; */ } /* * Save a copy of the IP header in case we want restore it * for sending an ICMP error message in response. */ save_ip = *ip; /* * Checksum extended UDP header and data. */ if (uh->uh_sum) { if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) uh->uh_sum = m->m_pkthdr.csum_data; else uh->uh_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htonl((u_short)len + m->m_pkthdr.csum_data + IPPROTO_UDP)); uh->uh_sum ^= 0xffff; } else { char b[9]; bcopy(((struct ipovly *)ip)->ih_x1, b, 9); bzero(((struct ipovly *)ip)->ih_x1, 9); ((struct ipovly *)ip)->ih_len = uh->uh_ulen; uh->uh_sum = in_cksum(m, len + sizeof(struct ip)); bcopy(b, ((struct ipovly *)ip)->ih_x1, 9); } if (uh->uh_sum) { udp_stat.udps_badsum++; m_freem(m); return(IPPROTO_DONE); } } else udp_stat.udps_nosum++; if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) || in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) { struct inpcbhead *connhead; struct inpcontainer *ic, *ic_marker; struct inpcontainerhead *ichead; struct udp_mcast_arg arg; struct inpcb *last; int error; /* * Deliver a multicast or broadcast datagram to *all* sockets * for which the local and remote addresses and ports match * those of the incoming datagram. This allows more than * one process to receive multi/broadcasts on the same port. * (This really ought to be done for unicast datagrams as * well, but that would cause problems with existing * applications that open both address-specific sockets and * a wildcard socket listening to the same port -- they would * end up receiving duplicates of every unicast datagram. * Those applications open the multiple sockets to overcome an * inadequacy of the UDP socket interface, but for backwards * compatibility we avoid the problem here rather than * fixing the interface. Maybe 4.5BSD will remedy this?) */ /* * Construct sockaddr format source address. */ udp_in.sin_port = uh->uh_sport; udp_in.sin_addr = ip->ip_src; arg.udp_in = &udp_in; /* * Locate pcb(s) for datagram. * (Algorithm copied from raw_intr().) */ last = NULL; arg.iphlen = iphlen; connhead = &pcbinfo->hashbase[ INP_PCBCONNHASH(ip->ip_src.s_addr, uh->uh_sport, ip->ip_dst.s_addr, uh->uh_dport, pcbinfo->hashmask)]; LIST_FOREACH(inp, connhead, inp_hash) { #ifdef INET6 if (!INP_ISIPV4(inp)) continue; #endif if (!in_hosteq(inp->inp_faddr, ip->ip_src) || !in_hosteq(inp->inp_laddr, ip->ip_dst) || inp->inp_fport != uh->uh_sport || inp->inp_lport != uh->uh_dport) continue; arg.inp = inp; arg.last = last; arg.ip = ip; arg.m = m; error = udp_mcast_input(&arg); if (error == ERESTART) continue; last = arg.last; if (error == EJUSTRETURN) goto done; } ichead = &pcbinfo->wildcardhashbase[ INP_PCBWILDCARDHASH(uh->uh_dport, pcbinfo->wildcardhashmask)]; ic_marker = in_pcbcontainer_marker(mycpuid); GET_PCBINFO_TOKEN(pcbinfo); LIST_INSERT_HEAD(ichead, ic_marker, ic_list); while ((ic = LIST_NEXT(ic_marker, ic_list)) != NULL) { LIST_REMOVE(ic_marker, ic_list); LIST_INSERT_AFTER(ic, ic_marker, ic_list); inp = ic->ic_inp; if (inp->inp_flags & INP_PLACEMARKER) continue; #ifdef INET6 if (!INP_ISIPV4(inp)) continue; #endif if (inp->inp_lport != uh->uh_dport) continue; if (inp->inp_laddr.s_addr != INADDR_ANY && inp->inp_laddr.s_addr != ip->ip_dst.s_addr) continue; arg.inp = inp; arg.last = last; arg.ip = ip; arg.m = m; error = udp_mcast_input(&arg); if (error == ERESTART) continue; last = arg.last; if (error == EJUSTRETURN) break; } LIST_REMOVE(ic_marker, ic_list); REL_PCBINFO_TOKEN(pcbinfo); done: if (last == NULL) { /* * No matching pcb found; discard datagram. * (No need to send an ICMP Port Unreachable * for a broadcast or multicast datgram.) */ udp_stat.udps_noportbcast++; goto bad; } #ifdef IPSEC /* check AH/ESP integrity. */ if (ipsec4_in_reject_so(m, last->inp_socket)) { ipsecstat.in_polvio++; goto bad; } #endif /*IPSEC*/ #ifdef FAST_IPSEC /* check AH/ESP integrity. */ if (ipsec4_in_reject(m, last)) goto bad; #endif /*FAST_IPSEC*/ udp_append(last, ip, m, iphlen + sizeof(struct udphdr), &udp_in); return(IPPROTO_DONE); } /* * Locate pcb for datagram. */ inp = in_pcblookup_pkthash(pcbinfo, ip->ip_src, uh->uh_sport, ip->ip_dst, uh->uh_dport, TRUE, m->m_pkthdr.rcvif, udp_reuseport_ext ? m : NULL); if (inp == NULL) { if (log_in_vain) { char buf[sizeof "aaa.bbb.ccc.ddd"]; strcpy(buf, inet_ntoa(ip->ip_dst)); log(LOG_INFO, "Connection attempt to UDP %s:%d from %s:%d\n", buf, ntohs(uh->uh_dport), inet_ntoa(ip->ip_src), ntohs(uh->uh_sport)); } udp_stat.udps_noport++; if (m->m_flags & (M_BCAST | M_MCAST)) { udp_stat.udps_noportbcast++; goto bad; } if (blackhole) goto bad; #ifdef ICMP_BANDLIM if (badport_bandlim(BANDLIM_ICMP_UNREACH) < 0) goto bad; #endif *ip = save_ip; ip->ip_len += iphlen; icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0); return(IPPROTO_DONE); } KASSERT(INP_ISIPV4(inp), ("not inet inpcb")); #ifdef IPSEC if (ipsec4_in_reject_so(m, inp->inp_socket)) { ipsecstat.in_polvio++; goto bad; } #endif /*IPSEC*/ #ifdef FAST_IPSEC if (ipsec4_in_reject(m, inp)) goto bad; #endif /*FAST_IPSEC*/ /* * Check the minimum TTL for socket. */ if (ip->ip_ttl < inp->inp_ip_minttl) goto bad; /* * Construct sockaddr format source address. * Stuff source address and datagram in user buffer. */ udp_in.sin_port = uh->uh_sport; udp_in.sin_addr = ip->ip_src; if ((inp->inp_flags & INP_CONTROLOPTS) || (inp->inp_socket->so_options & SO_TIMESTAMP)) ip_savecontrol(inp, &opts, ip, m); m_adj(m, iphlen + sizeof(struct udphdr)); lwkt_gettoken(&inp->inp_socket->so_rcv.ssb_token); if (ssb_appendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in, m, opts) == 0) { lwkt_reltoken(&inp->inp_socket->so_rcv.ssb_token); udp_stat.udps_fullsock++; goto bad; } lwkt_reltoken(&inp->inp_socket->so_rcv.ssb_token); sorwakeup(inp->inp_socket); return(IPPROTO_DONE); bad: m_freem(m); if (opts) m_freem(opts); return(IPPROTO_DONE); }
void udp_input(struct mbuf *m, int off) { int iphlen = off; struct ip *ip; struct udphdr *uh; struct ifnet *ifp; struct inpcb *inp; uint16_t len, ip_len; struct inpcbinfo *pcbinfo; struct ip save_ip; struct sockaddr_in udp_in; struct m_tag *fwd_tag; int cscov_partial; uint8_t pr; ifp = m->m_pkthdr.rcvif; UDPSTAT_INC(udps_ipackets); /* * Strip IP options, if any; should skip this, make available to * user, and use on returned packets, but we don't yet have a way to * check the checksum with options still present. */ if (iphlen > sizeof (struct ip)) { ip_stripoptions(m); iphlen = sizeof(struct ip); } /* * Get IP and UDP header together in first mbuf. */ ip = mtod(m, struct ip *); if (m->m_len < iphlen + sizeof(struct udphdr)) { if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == NULL) { UDPSTAT_INC(udps_hdrops); return; } ip = mtod(m, struct ip *); } uh = (struct udphdr *)((caddr_t)ip + iphlen); pr = ip->ip_p; cscov_partial = (pr == IPPROTO_UDPLITE) ? 1 : 0; /* * Destination port of 0 is illegal, based on RFC768. */ if (uh->uh_dport == 0) goto badunlocked; /* * Construct sockaddr format source address. Stuff source address * and datagram in user buffer. */ bzero(&udp_in, sizeof(udp_in)); udp_in.sin_len = sizeof(udp_in); udp_in.sin_family = AF_INET; udp_in.sin_port = uh->uh_sport; udp_in.sin_addr = ip->ip_src; /* * Make mbuf data length reflect UDP length. If not enough data to * reflect UDP length, drop. */ len = ntohs((u_short)uh->uh_ulen); ip_len = ntohs(ip->ip_len) - iphlen; if (pr == IPPROTO_UDPLITE && len == 0) { /* Zero means checksum over the complete packet. */ len = ip_len; cscov_partial = 0; } if (ip_len != len) { if (len > ip_len || len < sizeof(struct udphdr)) { UDPSTAT_INC(udps_badlen); goto badunlocked; } if (pr == IPPROTO_UDP) m_adj(m, len - ip_len); } /* * Save a copy of the IP header in case we want restore it for * sending an ICMP error message in response. */ if (!V_udp_blackhole) save_ip = *ip; else memset(&save_ip, 0, sizeof(save_ip)); /* * Checksum extended UDP header and data. */ if (uh->uh_sum) { u_short uh_sum; if ((m->m_pkthdr.csum_flags & CSUM_DATA_VALID) && !cscov_partial) { if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) uh_sum = m->m_pkthdr.csum_data; else uh_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htonl((u_short)len + m->m_pkthdr.csum_data + pr)); uh_sum ^= 0xffff; } else { char b[9]; bcopy(((struct ipovly *)ip)->ih_x1, b, 9); bzero(((struct ipovly *)ip)->ih_x1, 9); ((struct ipovly *)ip)->ih_len = (pr == IPPROTO_UDP) ? uh->uh_ulen : htons(ip_len); uh_sum = in_cksum(m, len + sizeof (struct ip)); bcopy(b, ((struct ipovly *)ip)->ih_x1, 9); } if (uh_sum) { UDPSTAT_INC(udps_badsum); m_freem(m); return; } } else UDPSTAT_INC(udps_nosum); pcbinfo = get_inpcbinfo(pr); if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) || in_broadcast(ip->ip_dst, ifp)) { struct inpcb *last; struct inpcbhead *pcblist; struct ip_moptions *imo; INP_INFO_RLOCK(pcbinfo); pcblist = get_pcblist(pr); last = NULL; LIST_FOREACH(inp, pcblist, inp_list) { if (inp->inp_lport != uh->uh_dport) continue; #ifdef INET6 if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif if (inp->inp_laddr.s_addr != INADDR_ANY && inp->inp_laddr.s_addr != ip->ip_dst.s_addr) continue; if (inp->inp_faddr.s_addr != INADDR_ANY && inp->inp_faddr.s_addr != ip->ip_src.s_addr) continue; if (inp->inp_fport != 0 && inp->inp_fport != uh->uh_sport) continue; INP_RLOCK(inp); /* * XXXRW: Because we weren't holding either the inpcb * or the hash lock when we checked for a match * before, we should probably recheck now that the * inpcb lock is held. */ /* * Handle socket delivery policy for any-source * and source-specific multicast. [RFC3678] */ imo = inp->inp_moptions; if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { struct sockaddr_in group; int blocked; if (imo == NULL) { INP_RUNLOCK(inp); continue; } bzero(&group, sizeof(struct sockaddr_in)); group.sin_len = sizeof(struct sockaddr_in); group.sin_family = AF_INET; group.sin_addr = ip->ip_dst; blocked = imo_multi_filter(imo, ifp, (struct sockaddr *)&group, (struct sockaddr *)&udp_in); if (blocked != MCAST_PASS) { if (blocked == MCAST_NOTGMEMBER) IPSTAT_INC(ips_notmember); if (blocked == MCAST_NOTSMEMBER || blocked == MCAST_MUTED) UDPSTAT_INC(udps_filtermcast); INP_RUNLOCK(inp); continue; } } if (last != NULL) { struct mbuf *n; n = m_copy(m, 0, M_COPYALL); udp_append(last, ip, n, iphlen, &udp_in); INP_RUNLOCK(last); } last = inp; /* * Don't look for additional matches if this one does * not have either the SO_REUSEPORT or SO_REUSEADDR * socket options set. This heuristic avoids * searching through all pcbs in the common case of a * non-shared port. It assumes that an application * will never clear these options after setting them. */ if ((last->inp_socket->so_options & (SO_REUSEPORT|SO_REUSEADDR)) == 0) break; } if (last == NULL) { /* * No matching pcb found; discard datagram. (No need * to send an ICMP Port Unreachable for a broadcast * or multicast datgram.) */ UDPSTAT_INC(udps_noportbcast); if (inp) INP_RUNLOCK(inp); INP_INFO_RUNLOCK(pcbinfo); goto badunlocked; } udp_append(last, ip, m, iphlen, &udp_in); INP_RUNLOCK(last); INP_INFO_RUNLOCK(pcbinfo); return; } /* * Locate pcb for datagram. */ /* * Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain. */ if ((m->m_flags & M_IP_NEXTHOP) && (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { struct sockaddr_in *next_hop; next_hop = (struct sockaddr_in *)(fwd_tag + 1); /* * Transparently forwarded. Pretend to be the destination. * Already got one like this? */ inp = in_pcblookup_mbuf(pcbinfo, ip->ip_src, uh->uh_sport, ip->ip_dst, uh->uh_dport, INPLOOKUP_RLOCKPCB, ifp, m); if (!inp) { /* * It's new. Try to find the ambushing socket. * Because we've rewritten the destination address, * any hardware-generated hash is ignored. */ inp = in_pcblookup(pcbinfo, ip->ip_src, uh->uh_sport, next_hop->sin_addr, next_hop->sin_port ? htons(next_hop->sin_port) : uh->uh_dport, INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB, ifp); } /* Remove the tag from the packet. We don't need it anymore. */ m_tag_delete(m, fwd_tag); m->m_flags &= ~M_IP_NEXTHOP; } else inp = in_pcblookup_mbuf(pcbinfo, ip->ip_src, uh->uh_sport, ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB, ifp, m); if (inp == NULL) { if (udp_log_in_vain) { char buf[4*sizeof "123"]; strcpy(buf, inet_ntoa(ip->ip_dst)); log(LOG_INFO, "Connection attempt to UDP %s:%d from %s:%d\n", buf, ntohs(uh->uh_dport), inet_ntoa(ip->ip_src), ntohs(uh->uh_sport)); } UDPSTAT_INC(udps_noport); if (m->m_flags & (M_BCAST | M_MCAST)) { UDPSTAT_INC(udps_noportbcast); goto badunlocked; } if (V_udp_blackhole) goto badunlocked; if (badport_bandlim(BANDLIM_ICMP_UNREACH) < 0) goto badunlocked; *ip = save_ip; icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0); return; } /* * Check the minimum TTL for socket. */ INP_RLOCK_ASSERT(inp); if (inp->inp_ip_minttl && inp->inp_ip_minttl > ip->ip_ttl) { INP_RUNLOCK(inp); m_freem(m); return; } if (cscov_partial) { struct udpcb *up; up = intoudpcb(inp); if (up->u_rxcslen > len) { INP_RUNLOCK(inp); m_freem(m); return; } } UDP_PROBE(receive, NULL, inp, ip, inp, uh); udp_append(inp, ip, m, iphlen, &udp_in); INP_RUNLOCK(inp); return; badunlocked: m_freem(m); }