Modify ip fw so that whenever UID or GID constraints exist in a

ruleset, the pcb is looked up once per ipfw_chk() activation.

This is done by extracting the required information out of the PCB
and caching it to the ipfw_chk() stack. This should greatly reduce
PCB looking contention and speed up the processing of UID/GID based
firewall rules (especially with large UID/GID rulesets).

Some very basic benchmarks were taken which compares the number
of in_pcblookup_hash(9) activations to the number of firewall
rules containing UID/GID based contraints before and after this patch.

The results can be viewed here:
o http://people.freebsd.org/~csjp/ip_fw_pcb.png

Reviewed by:	andre, luigi, rwatson
Approved by:	bmilekic (mentor)
This commit is contained in:
Christian S.J. Peron 2004-06-11 22:17:14 +00:00
parent 52fae0ba6c
commit d316f2cf4f

View File

@ -113,6 +113,18 @@ static int verbose_limit;
static struct callout ipfw_timeout; static struct callout ipfw_timeout;
#define IPFW_DEFAULT_RULE 65535 #define IPFW_DEFAULT_RULE 65535
/*
* Data structure to cache our ucred related
* information. This structure only gets used if
* the user specified UID/GID based constraints in
* a firewall rule.
*/
struct ip_fw_ugid {
gid_t fw_groups[NGROUPS];
int fw_ngroups;
uid_t fw_uid;
};
struct ip_fw_chain { struct ip_fw_chain {
struct ip_fw *rules; /* list of rules */ struct ip_fw *rules; /* list of rules */
struct ip_fw *reap; /* list of rules to reap */ struct ip_fw *reap; /* list of rules to reap */
@ -1529,13 +1541,23 @@ static int
check_uidgid(ipfw_insn_u32 *insn, check_uidgid(ipfw_insn_u32 *insn,
int proto, struct ifnet *oif, int proto, struct ifnet *oif,
struct in_addr dst_ip, u_int16_t dst_port, struct in_addr dst_ip, u_int16_t dst_port,
struct in_addr src_ip, u_int16_t src_port) struct in_addr src_ip, u_int16_t src_port,
struct ip_fw_ugid *ugp, int *lookup)
{ {
struct inpcbinfo *pi; struct inpcbinfo *pi;
int wildcard; int wildcard;
struct inpcb *pcb; struct inpcb *pcb;
int match; int match;
struct ucred *cr;
gid_t *gp;
/*
* If we have already been here and the packet has no
* PCB entry associated with it, then we can safely
* assume that this is a no match.
*/
if (*lookup == -1)
return (0);
if (proto == IPPROTO_TCP) { if (proto == IPPROTO_TCP) {
wildcard = 0; wildcard = 0;
pi = &tcbinfo; pi = &tcbinfo;
@ -1544,37 +1566,51 @@ check_uidgid(ipfw_insn_u32 *insn,
pi = &udbinfo; pi = &udbinfo;
} else } else
return 0; return 0;
match = 0; match = 0;
if (*lookup == 0) {
INP_INFO_RLOCK(pi); /* XXX LOR with IPFW */ INP_INFO_RLOCK(pi); /* XXX LOR with IPFW */
pcb = (oif) ? pcb = (oif) ?
in_pcblookup_hash(pi, in_pcblookup_hash(pi,
dst_ip, htons(dst_port), dst_ip, htons(dst_port),
src_ip, htons(src_port), src_ip, htons(src_port),
wildcard, oif) : wildcard, oif) :
in_pcblookup_hash(pi, in_pcblookup_hash(pi,
src_ip, htons(src_port), src_ip, htons(src_port),
dst_ip, htons(dst_port), dst_ip, htons(dst_port),
wildcard, NULL); wildcard, NULL);
if (pcb != NULL) { if (pcb != NULL) {
INP_LOCK(pcb); INP_LOCK(pcb);
if (pcb->inp_socket != NULL) { if (pcb->inp_socket != NULL) {
#if __FreeBSD_version < 500034 cr = pcb->inp_socket->so_cred;
#define socheckuid(a,b) ((a)->so_cred->cr_uid != (b)) ugp->fw_uid = cr->cr_uid;
#endif ugp->fw_ngroups = cr->cr_ngroups;
if (insn->o.opcode == O_UID) { bcopy(cr->cr_groups, ugp->fw_groups,
match = !socheckuid(pcb->inp_socket, sizeof(ugp->fw_groups));
(uid_t)insn->d[0]); *lookup = 1;
} else {
match = groupmember((uid_t)insn->d[0],
pcb->inp_socket->so_cred);
} }
INP_UNLOCK(pcb);
} }
INP_UNLOCK(pcb); INP_INFO_RUNLOCK(pi);
} if (*lookup == 0) {
INP_INFO_RUNLOCK(pi); /*
* If the lookup did not yield any results, there
* is no sense in coming back and trying again. So
* we can set lookup to -1 and ensure that we wont
* bother the pcb system again.
*/
*lookup = -1;
return (0);
}
}
if (insn->o.opcode == O_UID)
match = (ugp->fw_uid == (uid_t)insn->d[0]);
else if (insn->o.opcode == O_GID)
for (gp = ugp->fw_groups;
gp < &ugp->fw_groups[ugp->fw_ngroups]; gp++)
if (*gp == (gid_t)insn->d[0]) {
match = 1;
break;
}
return match; return match;
} }
@ -1641,6 +1677,16 @@ ipfw_chk(struct ip_fw_args *args)
struct mbuf *m = args->m; struct mbuf *m = args->m;
struct ip *ip = mtod(m, struct ip *); struct ip *ip = mtod(m, struct ip *);
/*
* For rules which contain uid/gid or jail constraints, cache
* a copy of the users credentials after the pcb lookup has been
* executed. This will speed up the processing of rules with
* these types of constraints, as well as decrease contention
* on pcb related locks.
*/
struct ip_fw_ugid fw_ugid_cache;
int ugid_lookup = 0;
/* /*
* oif | args->oif If NULL, ipfw_chk has been called on the * oif | args->oif If NULL, ipfw_chk has been called on the
* inbound path (ether_input, bdg_forward, ip_input). * inbound path (ether_input, bdg_forward, ip_input).
@ -1891,7 +1937,8 @@ ipfw_chk(struct ip_fw_args *args)
(ipfw_insn_u32 *)cmd, (ipfw_insn_u32 *)cmd,
proto, oif, proto, oif,
dst_ip, dst_port, dst_ip, dst_port,
src_ip, src_port); src_ip, src_port, &fw_ugid_cache,
&ugid_lookup);
break; break;
case O_RECV: case O_RECV: