s = splsoftnet();
pfsync_input(struct mbuf *m, ...){	struct ip *ip = mtod(m, struct ip *);	struct pfsync_header *ph;	struct pfsync_softc *sc = &pfsyncif;	struct pf_state *st, key;	struct pfsync_state *sp;	struct pfsync_state_upd *up;	struct pfsync_state_del *dp;	struct pfsync_state_clr *cp;	struct pfsync_state_upd_req *rup;	struct in_addr src;	struct mbuf *mp;	int iplen, action, error, i, s, count, offp;	pfsyncstats.pfsyncs_ipackets++;	/* verify that we have a sync interface configured */	if (!sc->sc_sync_ifp || !pf_status.running)		goto done;	/* verify that the packet came in on the right interface */	if (sc->sc_sync_ifp != m->m_pkthdr.rcvif) {		pfsyncstats.pfsyncs_badif++;		goto done;	}	/* verify that the IP TTL is 255.  */	if (ip->ip_ttl != PFSYNC_DFLTTL) {		pfsyncstats.pfsyncs_badttl++;		goto done;	}	iplen = ip->ip_hl << 2;	if (m->m_pkthdr.len < iplen + sizeof(*ph)) {		pfsyncstats.pfsyncs_hdrops++;		goto done;	}	if (iplen + sizeof(*ph) > m->m_len) {		if ((m = m_pullup(m, iplen + sizeof(*ph))) == NULL) {			pfsyncstats.pfsyncs_hdrops++;			goto done;		}		ip = mtod(m, struct ip *);	}	ph = (struct pfsync_header *)((char *)ip + iplen);	/* verify the version */	if (ph->version != PFSYNC_VERSION) {		pfsyncstats.pfsyncs_badver++;		goto done;	}	action = ph->action;	count = ph->count;	/* make sure it's a valid action code */	if (action >= PFSYNC_ACT_MAX) {		pfsyncstats.pfsyncs_badact++;		goto done;	}	/* Cheaper to grab this now than having to mess with mbufs later */	src = ip->ip_src;	switch (action) {	case PFSYNC_ACT_CLR: {		u_int32_t creatorid;		if ((mp = m_pulldown(m, iplen + sizeof(*ph),		    sizeof(*cp), &offp)) == NULL) {			pfsyncstats.pfsyncs_badlen++;			return;		}		s = splsoftnet();		cp = (struct pfsync_state_clr *)(mp->m_data + offp);		creatorid = cp->creatorid;		RB_FOREACH(st, pf_state_tree_id, &tree_id) {			if (st->creatorid == creatorid)				st->timeout = PFTM_PURGE;		}		pf_purge_expired_states();		splx(s);		break;	}	case PFSYNC_ACT_INS:		if ((mp = m_pulldown(m, iplen + sizeof(*ph),		    count * sizeof(*sp), &offp)) == NULL) {			pfsyncstats.pfsyncs_badlen++;			return;		}		s = splsoftnet();		for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp);		    i < count; i++, sp++) {			if ((error = pfsync_insert_net_state(sp))) {				if (error == ENOMEM) {					splx(s);					goto done;				}				continue;			}		}		splx(s);		break;	case PFSYNC_ACT_UPD:		if ((mp = m_pulldown(m, iplen + sizeof(*ph),		    count * sizeof(*sp), &offp)) == NULL) {			pfsyncstats.pfsyncs_badlen++;			return;		}		s = splsoftnet();		for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp);		    i < count; i++, sp++) {			bcopy(sp->id, &key.id, sizeof(key.id));			key.creatorid = sp->creatorid;			st = pf_find_state_byid(&key);			if (st == NULL) {				/* insert the update */				if (pfsync_insert_net_state(sp))					pfsyncstats.pfsyncs_badstate++;				continue;			}			pf_state_peer_ntoh(&sp->src, &st->src);			pf_state_peer_ntoh(&sp->dst, &st->dst);			st->expire = ntohl(sp->expire) + time.tv_sec;			st->timeout = sp->timeout;		}		splx(s);		break;	/*	 * It's not strictly necessary for us to support the "uncompressed"	 * delete action, but it's relatively simple and maintains consistency.	 */	case PFSYNC_ACT_DEL:		if ((mp = m_pulldown(m, iplen + sizeof(*ph),		    count * sizeof(*sp), &offp)) == NULL) {			pfsyncstats.pfsyncs_badlen++;			return;		}		s = splsoftnet();		for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp);		    i < count; i++, sp++) {			bcopy(sp->id, &key.id, sizeof(key.id));			key.creatorid = sp->creatorid;			st = pf_find_state_byid(&key);			if (st == NULL) {				pfsyncstats.pfsyncs_badstate++;				continue;			}			/*			 * XXX			 * pf_purge_expired_states() is expensive,			 * we really want to purge the state directly.			 */			st->timeout = PFTM_PURGE;			st->sync_flags |= PFSTATE_FROMSYNC;		}		pf_purge_expired_states();		splx(s);		break;	case PFSYNC_ACT_UPD_C: {		int update_requested = 0;		if ((mp = m_pulldown(m, iplen + sizeof(*ph),		    count * sizeof(*up), &offp)) == NULL) {			pfsyncstats.pfsyncs_badlen++;			return;		}		s = splsoftnet();		for (i = 0, up = (struct pfsync_state_upd *)(mp->m_data + offp);		    i < count; i++, up++) {			bcopy(up->id, &key.id, sizeof(key.id));			key.creatorid = up->creatorid;			st = pf_find_state_byid(&key);			if (st == NULL) {				/* We don't have this state. Ask for it. */				pfsync_request_update(up, &src);				update_requested = 1;				pfsyncstats.pfsyncs_badstate++;				continue;			}			pf_state_peer_ntoh(&up->src, &st->src);			pf_state_peer_ntoh(&up->dst, &st->dst);			st->expire = ntohl(up->expire) + time.tv_sec;			st->timeout = up->timeout;		}		if (update_requested)			pfsync_sendout(sc);		splx(s);		break;	}	case PFSYNC_ACT_DEL_C:		if ((mp = m_pulldown(m, iplen + sizeof(*ph),		    count * sizeof(*dp), &offp)) == NULL) {			pfsyncstats.pfsyncs_badlen++;			return;		}		s = splsoftnet();		for (i = 0, dp = (struct pfsync_state_del *)(mp->m_data + offp);		    i < count; i++, dp++) {			bcopy(dp->id, &key.id, sizeof(key.id));			key.creatorid = dp->creatorid;			st = pf_find_state_byid(&key);			if (st == NULL) {				pfsyncstats.pfsyncs_badstate++;				continue;			}			/*			 * XXX			 * pf_purge_expired_states() is expensive,			 * we really want to purge the state directly.			 */			st->timeout = PFTM_PURGE;			st->sync_flags |= PFSTATE_FROMSYNC;		}		pf_purge_expired_states();		splx(s);		break;	case PFSYNC_ACT_INS_F:	case PFSYNC_ACT_DEL_F:		/* not implemented */		break;	case PFSYNC_ACT_UREQ:		if ((mp = m_pulldown(m, iplen + sizeof(*ph),		    count * sizeof(*rup), &offp)) == NULL) {			pfsyncstats.pfsyncs_badlen++;			return;		}		s = splsoftnet();		/* XXX send existing. pfsync_pack_state should handle this. */		if (sc->sc_mbuf != NULL)			pfsync_sendout(sc);		for (i = 0,		    rup = (struct pfsync_state_upd_req *)(mp->m_data + offp);		    i < count; i++, rup++) {			bcopy(rup->id, &key.id, sizeof(key.id));			key.creatorid = rup->creatorid;			st = pf_find_state_byid(&key);			if (st == NULL) {				pfsyncstats.pfsyncs_badstate++;				continue;			}			pfsync_pack_state(PFSYNC_ACT_UPD, st, 0);		}		if (sc->sc_mbuf != NULL)			pfsync_sendout(sc);		splx(s);		break;	}done:	if (m)		m_freem(m);}