Hello,

it has turned out things are bit more complicated when it comes to interface
groups. diff below makes following scenario work for me.

    we start with etc/pf.conf as follows:

        # cat /etc/pf.conf
        set skip on lo
        set skip on test1
        set skip on test2
        set skip on agroup
        block return

    Let's see what interfaces are known to pf(4)

        # pfctl -sI
        agroup
        all
        carp
        egress
        enc
        enc0
        lo
        lo0
        pflog
        pflog0
        test1
        test2
        vio0

    Using the same rules on current we get different output:

        # pfctl -sI
        all                                                                     
                                                                                
                                                                                
   
        carp                                                                    
                                                                                
                                                                                
   
        egress                                                                  
                                                                                
                                                                                
   
        enc                                                                     
                                                                                
                                                                                
   
        enc0                                                                    
                                                                                
                                                                                
   
        lo                                                                      
                                                                                
                                                                                
   
        lo0                                                                     
                                                                                
                                                                                
   
        pflog                                                                   
                                                                                
                                                                                
   
        pflog0                                                                  
                                                                                
                                                                                
   
        vio0

    as you can see test1, test2 and agroup are missing on current.
    it's because the current does not create a pfi_kif entry for
    interfaces/groups referred by 'set skip on' keyword in pf.conf.

    the rules found in /etc/pf.conf block all network traffic:

        # ping -c 1 192.168.2.1 
        PING 192.168.2.1 (192.168.2.1): 56 data bytes
        ping: sendmsg: Permission denied
        ping: wrote 192.168.2.1 64 chars, ret=-1

        --- 192.168.2.1 ping statistics ---
        1 packets transmitted, 0 packets received, 100.0% packet loss

    to demonstrate 'set skip on agroup' works I'll add vio0 interface into
    agroup and repeat test:

        # ifconfig vio0 group agroup
        # ping -c 1 192.168.2.1 
        PING 192.168.2.1 (192.168.2.1): 56 data bytes
        64 bytes from 192.168.2.1: icmp_seq=0 ttl=254 time=1.058 ms

        --- 192.168.2.1 ping statistics ---
        1 packets transmitted, 1 packets received, 0.0% packet loss
        round-trip min/avg/max/std-dev = 1.058/1.058/1.058/0.000 ms

    removing vio0 from agroup stops network again:

        # ifconfig vio0 -group agroup                                           
        # ping -c 1 192.168.2.1       
        PING 192.168.2.1 (192.168.2.1): 56 data bytes
        ping: sendmsg: Permission denied
        ping: wrote 192.168.2.1 64 chars, ret=-1

        --- 192.168.2.1 ping statistics ---
        1 packets transmitted, 0 packets received, 100.0% packet loss

    removing 'set skip on agroup' from /etc/pf.conf also removes
    the matching pfi_kif object from interface table kept by pf(4):

        # cat /etc/pf.conf
        set skip on lo
        set skip on test1
        set skip on test2
        block return
        # pfctl -f /etc/pf.conf
        # pfctl -sI
        all
        carp
        egress
        enc
        enc0
        lo
        lo0
        pflog
        pflog0
        test1
        test2
        vio0


The main trick to get things to work is to let 'set skip on ...' create entries
(pfi_kif objects)) in pf's table for interfaces and groups which are not known
to IP stack yet. The same thing happens when firewall rule refers interface,
which does not exist in system yet. Diff below modifies pfi_set_flags()
function to create pfi_kif object if it does not already. We also obtain
a reference PFI_KIF_REF_FLAG if and only if the flag is being changed.

We treat a pfik_reflag reference counter as an indication whether the pfi_kif
object get created on behalf of 'set skip on ...' keyword, so we can call
pfi_kif_unref() in pfi_clear_flags() to destroy it. The pfik_reflag also
prevents other callers to pfi_kif_unref() from destroying pfi_kif, which
got created by pfi_set_flags().

Changes described so far are sufficient to get 'set skip on...' working for
regular interfaces. To make it working for interface group we need to introduce
pfi_group_delmember() and slightly adjust pfi_group_addmember().
Both functions update interface flag in the same fashion like pfi_set_flags()
and pfi_clear_flags(). The caller of pfi_group_delmember()/pfi_group_addmember()
must call pfi_xcommit() to update interfaces.

OK?

thanks and
regards
sashan

--------8<---------------8<---------------8<------------------8<--------
diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5
index bff448aa8dc..4afe841651f 100644
--- a/share/man/man5/pf.conf.5
+++ b/share/man/man5/pf.conf.5
@@ -1383,9 +1383,6 @@ Packets passing in or out on such interfaces are passed 
as if pf was
 disabled, i.e. pf does not process them in any way.
 This can be useful on loopback and other virtual interfaces, when
 packet filtering is not desired and can have unexpected effects.
-.Ar ifspec
-is only evaluated when the ruleset is loaded; interfaces created
-later will not be skipped.
 PF filters traffic on all interfaces by default.
 .It Ic set Cm state-defaults Ar state-option , ...
 The
diff --git a/sys/net/if.c b/sys/net/if.c
index 2e9a968d7cc..a6d3cb4f4ac 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -2725,6 +2725,7 @@ if_addgroup(struct ifnet *ifp, const char *groupname)
 
 #if NPF > 0
        pfi_group_addmember(groupname, ifp);
+       pfi_xcommit();
 #endif
 
        return (0);
@@ -2757,7 +2758,7 @@ if_delgroup(struct ifnet *ifp, const char *groupname)
        }
 
 #if NPF > 0
-       pfi_group_change(groupname);
+       pfi_group_delmember(groupname, ifp);
 #endif
 
        KASSERT(ifgl->ifgl_group->ifg_refcnt != 0);
@@ -2769,6 +2770,10 @@ if_delgroup(struct ifnet *ifp, const char *groupname)
                free(ifgl->ifgl_group, M_TEMP, sizeof(*ifgl->ifgl_group));
        }
 
+#if NPF > 0
+       pfi_xcommit();
+#endif
+
        free(ifgl, M_TEMP, sizeof(*ifgl));
 
        return (0);
diff --git a/sys/net/pf_if.c b/sys/net/pf_if.c
index 8de37375ab4..4fed66d9bf3 100644
--- a/sys/net/pf_if.c
+++ b/sys/net/pf_if.c
@@ -75,6 +75,7 @@ void           pfi_address_add(struct sockaddr *, 
sa_family_t, u_int8_t);
 int             pfi_if_compare(struct pfi_kif *, struct pfi_kif *);
 int             pfi_skip_if(const char *, struct pfi_kif *);
 int             pfi_unmask(void *);
+void            pfi_group_change(const char *);
 
 RB_PROTOTYPE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare);
 RB_GENERATE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare);
@@ -187,6 +188,9 @@ pfi_kif_ref(struct pfi_kif *kif, enum pfi_kif_refs what)
        case PFI_KIF_REF_SRCNODE:
                kif->pfik_srcnodes++;
                break;
+       case PFI_KIF_REF_FLAG:
+               kif->pfik_flagrefs++;
+               break;
        default:
                panic("pfi_kif_ref with unknown type");
        }
@@ -204,7 +208,8 @@ pfi_kif_unref(struct pfi_kif *kif, enum pfi_kif_refs what)
        case PFI_KIF_REF_RULE:
                if (kif->pfik_rules <= 0) {
                        DPFPRINTF(LOG_ERR,
-                           "pfi_kif_unref: rules refcount <= 0");
+                           "pfi_kif_unref (%s): rules refcount <= 0",
+                           kif->pfik_name);
                        return;
                }
                kif->pfik_rules--;
@@ -212,7 +217,8 @@ pfi_kif_unref(struct pfi_kif *kif, enum pfi_kif_refs what)
        case PFI_KIF_REF_STATE:
                if (kif->pfik_states <= 0) {
                        DPFPRINTF(LOG_ERR,
-                           "pfi_kif_unref: state refcount <= 0");
+                           "pfi_kif_unref (%s): state refcount <= 0",
+                           kif->pfik_name);
                        return;
                }
                kif->pfik_states--;
@@ -220,7 +226,8 @@ pfi_kif_unref(struct pfi_kif *kif, enum pfi_kif_refs what)
        case PFI_KIF_REF_ROUTE:
                if (kif->pfik_routes <= 0) {
                        DPFPRINTF(LOG_ERR,
-                           "pfi_kif_unref: route refcount <= 0");
+                           "pfi_kif_unref (%s): route refcount <= 0",
+                           kif->pfik_name);
                        return;
                }
                kif->pfik_routes--;
@@ -228,20 +235,30 @@ pfi_kif_unref(struct pfi_kif *kif, enum pfi_kif_refs what)
        case PFI_KIF_REF_SRCNODE:
                if (kif->pfik_srcnodes <= 0) {
                        DPFPRINTF(LOG_ERR,
-                           "pfi_kif_unref: src-node refcount <= 0");
+                           "pfi_kif_unref (%s): src-node refcount <= 0",
+                           kif->pfik_name);
                        return;
                }
                kif->pfik_srcnodes--;
                break;
+       case PFI_KIF_REF_FLAG:
+               if (kif->pfik_flagrefs <= 0) {
+                       DPFPRINTF(LOG_ERR,
+                           "pfi_kif_unref (%s): flags refcount <= 0",
+                           kif->pfik_name);
+                       return;
+               }
+               kif->pfik_flagrefs--;
+               break;
        default:
-               panic("pfi_kif_unref with unknown type");
+               panic("pfi_kif_unref (%s) with unknown type", kif->pfik_name);
        }
 
-       if (kif->pfik_ifp != NULL || kif->pfik_group != NULL || kif == pfi_all)
+       if (kif->pfik_ifp != NULL || kif->pfik_group != NULL ||kif == pfi_all)
                return;
 
        if (kif->pfik_rules || kif->pfik_states || kif->pfik_routes ||
-           kif->pfik_srcnodes)
+           kif->pfik_srcnodes || kif->pfik_flagrefs)
                return;
 
        RB_REMOVE(pfi_ifhead, &pfi_ifs, kif);
@@ -353,6 +370,19 @@ pfi_group_change(const char *group)
        pfi_kif_update(kif);
 }
 
+void
+pfi_group_delmember(const char *group, struct ifnet *ifp)
+{
+       struct pfi_kif          *gkif, *ikif;
+
+       if ((gkif = pfi_kif_get(group, NULL)) == NULL ||
+           (ikif = pfi_kif_get(ifp->if_xname, NULL)) == NULL)
+               panic("%s: pfi_kif_get failed", __func__);
+       ikif->pfik_flags_new = ikif->pfik_flags & ~gkif->pfik_flags;
+
+       pfi_group_change(group);
+}
+
 void
 pfi_group_addmember(const char *group, struct ifnet *ifp)
 {
@@ -361,7 +391,7 @@ pfi_group_addmember(const char *group, struct ifnet *ifp)
        if ((gkif = pfi_kif_get(group, NULL)) == NULL ||
            (ikif = pfi_kif_get(ifp->if_xname, NULL)) == NULL)
                panic("%s: pfi_kif_get failed", __func__);
-       ikif->pfik_flags |= gkif->pfik_flags;
+       ikif->pfik_flags_new = ikif->pfik_flags | gkif->pfik_flags;
 
        pfi_group_change(group);        
 }
@@ -786,25 +816,65 @@ int
 pfi_set_flags(const char *name, int flags)
 {
        struct pfi_kif  *p;
+       int found = 0;
+       size_t n;
 
        RB_FOREACH(p, pfi_ifhead, &pfi_ifs) {
                if (pfi_skip_if(name, p))
                        continue;
                p->pfik_flags_new = p->pfik_flags | flags;
+               found = 1;
        }
+
+       if (found == 0) {
+               if (name == NULL)
+                       return (0);
+
+               n = strlen(name);
+               if ((n < 1) || (n >= IFNAMSIZ))
+                       return (0);
+
+               if (((name[0] < 'a') || (name[0] > 'z')) ||
+                   ((name[0] < 'A') && (name[0] > 'Z')))
+                       return (0);
+
+               p = pfi_kif_get(name, NULL);
+               if (p != NULL) {
+                       p->pfik_flags_new = p->pfik_flags | flags;
+
+                       /*
+                        * We use pfik_flagrefs counter as an indication
+                        * whether the kif has been created on behalf of
+                        * 'pfi_set_flags()' or not.
+                        */
+                       KASSERT((p->pfik_flagrefs == 0) ||
+                           (p->pfik_flagrefs == 1));
+
+                       if ((p->pfik_flags_new != p->pfik_flags) &&
+                           (p->pfik_flagrefs == 0))
+                               pfi_kif_ref(p, PFI_KIF_REF_FLAG);
+               }
+       }
+
        return (0);
 }
 
 int
 pfi_clear_flags(const char *name, int flags)
 {
-       struct pfi_kif  *p;
+       struct pfi_kif  *p, *w;
 
-       RB_FOREACH(p, pfi_ifhead, &pfi_ifs) {
+       RB_FOREACH_SAFE(p, pfi_ifhead, &pfi_ifs, w) {
                if (pfi_skip_if(name, p))
                        continue;
+
+               KASSERT((p->pfik_flagrefs == 0) || (p->pfik_flagrefs == 1));
                p->pfik_flags_new = p->pfik_flags & ~flags;
+               if ((p->pfik_flags_new != p->pfik_flags) &&
+                   (p->pfik_flagrefs == 1))
+                       pfi_kif_unref(p, PFI_KIF_REF_FLAG);
        }
+
        return (0);
 }
 
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 514b9e156f3..61fb6b9436f 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1171,6 +1171,7 @@ struct pfi_kif {
        int                              pfik_rules;
        int                              pfik_routes;
        int                              pfik_srcnodes;
+       int                              pfik_flagrefs;
        TAILQ_HEAD(, pfi_dynaddr)        pfik_dynaddrs;
 };
 
@@ -1179,7 +1180,8 @@ enum pfi_kif_refs {
        PFI_KIF_REF_STATE,
        PFI_KIF_REF_RULE,
        PFI_KIF_REF_ROUTE,
-       PFI_KIF_REF_SRCNODE
+       PFI_KIF_REF_SRCNODE,
+       PFI_KIF_REF_FLAG
 };
 
 #define PFI_IFLAG_SKIP         0x0100  /* skip filtering on interface */
@@ -1867,8 +1869,8 @@ void               pfi_attach_ifnet(struct ifnet *);
 void            pfi_detach_ifnet(struct ifnet *);
 void            pfi_attach_ifgroup(struct ifg_group *);
 void            pfi_detach_ifgroup(struct ifg_group *);
-void            pfi_group_change(const char *);
 void            pfi_group_addmember(const char *, struct ifnet *);
+void            pfi_group_delmember(const char *, struct ifnet *);
 int             pfi_match_addr(struct pfi_dynaddr *, struct pf_addr *,
                    sa_family_t);
 int             pfi_dynaddr_setup(struct pf_addr_wrap *, sa_family_t);

to 

Reply via email to