static void in_rtqtimo(void *rock) { struct radix_node_head *rnh = rock; struct rtqk_arg arg; struct timeval atv; static time_t last_adjusted_timeout = 0; int s; arg.found = arg.killed = 0; arg.rnh = rnh; arg.nextstop = rtems_bsdnet_seconds_since_boot() + rtq_timeout; arg.draining = arg.updating = 0; s = splnet(); rnh->rnh_walktree(rnh, in_rtqkill, &arg); splx(s); /* * Attempt to be somewhat dynamic about this: * If there are ``too many'' routes sitting around taking up space, * then crank down the timeout, and see if we can't make some more * go away. However, we make sure that we will never adjust more * than once in rtq_timeout seconds, to keep from cranking down too * hard. */ if((arg.found - arg.killed > rtq_toomany) && (rtems_bsdnet_seconds_since_boot() - last_adjusted_timeout >= rtq_timeout) && rtq_reallyold > rtq_minreallyold) { rtq_reallyold = 2*rtq_reallyold / 3; if(rtq_reallyold < rtq_minreallyold) { rtq_reallyold = rtq_minreallyold; } last_adjusted_timeout = rtems_bsdnet_seconds_since_boot(); #ifdef DIAGNOSTIC log(LOG_DEBUG, "in_rtqtimo: adjusted rtq_reallyold to %d\n", rtq_reallyold); #endif arg.found = arg.killed = 0; arg.updating = 1; s = splnet(); rnh->rnh_walktree(rnh, in_rtqkill, &arg); splx(s); } atv.tv_usec = 0; atv.tv_sec = arg.nextstop; timeout(in_rtqtimo, rock, hzto(&atv)); }
/* * Return a random connection that hasn't been serviced yet and * is eligible for discard. There is a one in qlen chance that * we will return a null, saying that there are no dropable * requests. In this case, the protocol specific code should drop * the new request. This insures fairness. * * This may be used in conjunction with protocol specific queue * congestion routines. */ struct socket * sodropablereq(struct socket *head) { register struct socket *so; uint32_t i, j, qlen, m; static int rnd; static long old_mono_secs; static unsigned int cur_cnt, old_cnt; if ((i = (m = rtems_bsdnet_seconds_since_boot()) - old_mono_secs) != 0) { old_mono_secs = m; old_cnt = cur_cnt / i; cur_cnt = 0; } so = TAILQ_FIRST(&head->so_incomp); if (!so) return (so); qlen = head->so_incqlen; if (++cur_cnt > qlen || old_cnt > qlen) { rnd = (314159 * rnd + 66329) & 0xffff; j = ((qlen + 1) * rnd) >> 16; while (j-- && so) so = TAILQ_NEXT(so, so_list); }
/* * On last reference drop, mark the route as belong to us so that it can be * timed out. */ static void in_clsroute(struct radix_node *rn, struct radix_node_head *head) { struct rtentry *rt = (struct rtentry *)rn; if(!(rt->rt_flags & RTF_UP)) return; /* prophylactic measures */ if((rt->rt_flags & (RTF_LLINFO | RTF_HOST)) != RTF_HOST) return; if((rt->rt_flags & (RTF_WASCLONED | RTPRF_OURS)) != RTF_WASCLONED) return; /* * As requested by David Greenman: * If rtq_reallyold is 0, just delete the route without * waiting for a timeout cycle to kill it. */ if(rtq_reallyold != 0) { rt->rt_flags |= RTPRF_OURS; rt->rt_rmx.rmx_expire = rtems_bsdnet_seconds_since_boot() + rtq_reallyold; } else { rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt), rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0); } }
/* * Get rid of old routes. When draining, this deletes everything, even when * the timeout is not expired yet. When updating, this makes sure that * nothing has a timeout longer than the current value of rtq_reallyold. */ static int in_rtqkill(struct radix_node *rn, void *rock) { struct rtqk_arg *ap = rock; struct rtentry *rt = (struct rtentry *)rn; int err; if(rt->rt_flags & RTPRF_OURS) { ap->found++; if(ap->draining || rt->rt_rmx.rmx_expire <= rtems_bsdnet_seconds_since_boot()) { if(rt->rt_refcnt > 0) panic("rtqkill route really not free"); err = rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt), rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0); if(err) { log(LOG_WARNING, "in_rtqkill: error %d\n", err); } else { ap->killed++; } } else { if(ap->updating && (rt->rt_rmx.rmx_expire - rtems_bsdnet_seconds_since_boot() > rtq_reallyold)) { rt->rt_rmx.rmx_expire = rtems_bsdnet_seconds_since_boot() + rtq_reallyold; } ap->nextstop = lmin(ap->nextstop, rt->rt_rmx.rmx_expire); } } return 0; }
static int ip_fw_chk(struct ip **pip, int hlen, struct ifnet *oif, int ignport, struct mbuf **m) { struct ip_fw_chain *chain; struct ip_fw *rule = NULL; struct ip *ip = *pip; struct ifnet *const rif = (*m)->m_pkthdr.rcvif; u_short offset = (ip->ip_off & IP_OFFMASK); u_short src_port, dst_port; /* * Go down the chain, looking for enlightment */ for (chain=ip_fw_chain.lh_first; chain; chain = chain->chain.le_next) { register struct ip_fw *const f = chain->rule; /* Check direction inbound */ if (!oif && !(f->fw_flg & IP_FW_F_IN)) continue; /* Check direction outbound */ if (oif && !(f->fw_flg & IP_FW_F_OUT)) continue; /* Fragments */ if ((f->fw_flg & IP_FW_F_FRAG) && !(ip->ip_off & IP_OFFMASK)) continue; /* If src-addr doesn't match, not this rule. */ if (((f->fw_flg & IP_FW_F_INVSRC) != 0) ^ ((ip->ip_src.s_addr & f->fw_smsk.s_addr) != f->fw_src.s_addr)) continue; /* If dest-addr doesn't match, not this rule. */ if (((f->fw_flg & IP_FW_F_INVDST) != 0) ^ ((ip->ip_dst.s_addr & f->fw_dmsk.s_addr) != f->fw_dst.s_addr)) continue; /* Interface check */ if ((f->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) { struct ifnet *const iface = oif ? oif : rif; /* Backwards compatibility hack for "via" */ if (!iface || !iface_match(iface, &f->fw_in_if, f->fw_flg & IP_FW_F_OIFNAME)) continue; } else { /* Check receive interface */ if ((f->fw_flg & IP_FW_F_IIFACE) && (!rif || !iface_match(rif, &f->fw_in_if, f->fw_flg & IP_FW_F_IIFNAME))) continue; /* Check outgoing interface */ if ((f->fw_flg & IP_FW_F_OIFACE) && (!oif || !iface_match(oif, &f->fw_out_if, f->fw_flg & IP_FW_F_OIFNAME))) continue; } /* Check IP options */ if (f->fw_ipopt != f->fw_ipnopt && !ipopts_match(ip, f)) continue; /* Check protocol; if wildcard, match */ if (f->fw_prot == IPPROTO_IP) goto got_match; /* If different, don't match */ if (ip->ip_p != f->fw_prot) continue; #define PULLUP_TO(len) do { \ if ((*m)->m_len < (len) \ && (*m = m_pullup(*m, (len))) == 0) { \ goto bogusfrag; \ } \ *pip = ip = mtod(*m, struct ip *); \ offset = (ip->ip_off & IP_OFFMASK); \ } while (0) /* Protocol specific checks */ switch (ip->ip_p) { case IPPROTO_TCP: { struct tcphdr *tcp; if (offset == 1) /* cf. RFC 1858 */ goto bogusfrag; if (offset != 0) { /* * TCP flags and ports aren't available in this * packet -- if this rule specified either one, * we consider the rule a non-match. */ if (f->fw_nports != 0 || f->fw_tcpf != f->fw_tcpnf) continue; break; } PULLUP_TO(hlen + 14); tcp = (struct tcphdr *) ((u_long *)ip + ip->ip_hl); if (f->fw_tcpf != f->fw_tcpnf && !tcpflg_match(tcp, f)) continue; src_port = ntohs(tcp->th_sport); dst_port = ntohs(tcp->th_dport); goto check_ports; } case IPPROTO_UDP: { struct udphdr *udp; if (offset != 0) { /* * Port specification is unavailable -- if this * rule specifies a port, we consider the rule * a non-match. */ if (f->fw_nports != 0) continue; break; } PULLUP_TO(hlen + 4); udp = (struct udphdr *) ((u_long *)ip + ip->ip_hl); src_port = ntohs(udp->uh_sport); dst_port = ntohs(udp->uh_dport); check_ports: if (!port_match(&f->fw_pts[0], IP_FW_GETNSRCP(f), src_port, f->fw_flg & IP_FW_F_SRNG)) continue; if (!port_match(&f->fw_pts[IP_FW_GETNSRCP(f)], IP_FW_GETNDSTP(f), dst_port, f->fw_flg & IP_FW_F_DRNG)) continue; break; } case IPPROTO_ICMP: { struct icmp *icmp; if (offset != 0) /* Type isn't valid */ break; PULLUP_TO(hlen + 2); icmp = (struct icmp *) ((u_long *)ip + ip->ip_hl); if (!icmptype_match(icmp, f)) continue; break; } #undef PULLUP_TO bogusfrag: if (fw_verbose) ipfw_report(NULL, ip, rif, oif); goto dropit; } got_match: /* Ignore divert/tee rule if socket port is "ignport" */ switch (f->fw_flg & IP_FW_F_COMMAND) { case IP_FW_F_DIVERT: case IP_FW_F_TEE: if (f->fw_divert_port == ignport) continue; /* ignore this rule */ break; } /* Update statistics */ f->fw_pcnt += 1; f->fw_bcnt += ip->ip_len; f->timestamp = rtems_bsdnet_seconds_since_boot(); /* Log to console if desired */ if ((f->fw_flg & IP_FW_F_PRN) && fw_verbose) ipfw_report(f, ip, rif, oif); /* Take appropriate action */ switch (f->fw_flg & IP_FW_F_COMMAND) { case IP_FW_F_ACCEPT: return(0); case IP_FW_F_COUNT: continue; case IP_FW_F_DIVERT: return(f->fw_divert_port); case IP_FW_F_TEE: /* * XXX someday tee packet here, but beware that you * can't use m_copym() or m_copypacket() because * the divert input routine modifies the mbuf * (and these routines only increment reference * counts in the case of mbuf clusters), so need * to write custom routine. */ continue; case IP_FW_F_SKIPTO: #ifdef DIAGNOSTIC while (chain->chain.le_next && chain->chain.le_next->rule->fw_number < f->fw_skipto_rule) #else while (chain->chain.le_next->rule->fw_number < f->fw_skipto_rule) #endif chain = chain->chain.le_next; continue; } /* Deny/reject this packet using this rule */ rule = f; break; } #ifdef DIAGNOSTIC /* Rule 65535 should always be there and should always match */ if (!chain) panic("ip_fw: chain"); #endif /* * At this point, we're going to drop the packet. * Send a reject notice if all of the following are true: * * - The packet matched a reject rule * - The packet is not an ICMP packet * - The packet is not a multicast or broadcast packet */ if ((rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT && ip->ip_p != IPPROTO_ICMP && !((*m)->m_flags & (M_BCAST|M_MCAST)) && !IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { switch (rule->fw_reject_code) { case IP_FW_REJECT_RST: { struct tcphdr *const tcp = (struct tcphdr *) ((u_long *)ip + ip->ip_hl); struct tcpiphdr ti, *const tip = (struct tcpiphdr *) ip; if (offset != 0 || (tcp->th_flags & TH_RST)) break; ti.ti_i = *((struct ipovly *) ip); ti.ti_t = *tcp; bcopy(&ti, ip, sizeof(ti)); NTOHL(tip->ti_seq); NTOHL(tip->ti_ack); tip->ti_len = ip->ip_len - hlen - (tip->ti_off << 2); if (tcp->th_flags & TH_ACK) { tcp_respond(NULL, tip, *m, (tcp_seq)0, ntohl(tcp->th_ack), TH_RST); } else { if (tcp->th_flags & TH_SYN) tip->ti_len++; tcp_respond(NULL, tip, *m, tip->ti_seq + tip->ti_len, (tcp_seq)0, TH_RST|TH_ACK); } *m = NULL; break; } default: /* Send an ICMP unreachable using code */ icmp_error(*m, ICMP_UNREACH, rule->fw_reject_code, 0L, 0); *m = NULL; break; } } dropit: /* * Finally, drop the packet. */ if (*m) { m_freem(*m); *m = NULL; } return(0); }