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:
parent
52fae0ba6c
commit
d316f2cf4f
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user