On Sun, Jul 03, 2022 at 09:05:28AM +0200, Alexander Hall wrote:
> On Sat, Jul 02, 2022 at 08:12:29PM +0000, Klemens Nanni wrote:
> > On Sat, Jul 02, 2022 at 03:00:00PM +0200, Alexander Hall wrote:
> > > On Thu, Jun 30, 2022 at 03:35:05PM +0000, Klemens Nanni wrote:
> > > > On Tue, Dec 07, 2021 at 08:15:41PM +0000, Klemens Nanni wrote:
> > > > > On Tue, Nov 23, 2021 at 01:17:14AM +0000, Klemens Nanni wrote:
> > > > > > On Tue, Nov 16, 2021 at 11:09:40PM +0000, Klemens Nanni wrote:
> > > > > > > Run on boot without arguments, netstart(8) creates all virtual
> > > > > > > interfaces *for which hostname.if files exist* before configuring 
> > > > > > > them.
> > > > > > > 
> > > > > > > This prevents ordering problems with bridges and its members, as 
> > > > > > > dlg's
> > > > > > > commit message from 2018 reminds us.
> > > > > > > 
> > > > > > > But it also helps interface types like pair(4) which pair one 
> > > > > > > another
> > > > > > > in whatever way the user says:
> > > > > > > 
> > > > > > >   $ cat /etc/hostname.pair1
> > > > > > >   patch pair2
> > > > > > >   $ cat /etc/hostname.pair2
> > > > > > >   rdomain 1
> > > > > > > 
> > > > > > > On boot this works, but `sh /etc/netstart pair1 pair2' won't work
> > > > > > > because pair2 does not exist a creation time of pair1 because 
> > > > > > > netstart
> > > > > > > does not create virtual interfaces upfront.
> > > > > > > 
> > > > > > > I just hit this exact use case when setting up gelatod(8) (see 
> > > > > > > ports@).
> > > > > > > 
> > > > > > > To fix this, pass the list of interfaces to vifscreate() and make 
> > > > > > > it
> > > > > > > create only those iff given.
> > > > > > > 
> > > > > > > Regular boot, i.e. `sh /etc/netstart', stays uneffected by this 
> > > > > > > and
> > > > > > > selective runs as shown work as expected without requring users 
> > > > > > > to know
> > > > > > > the order in which netstart creates/configures interfaces.
> > > > > > > 
> > > > > > > The installer's internal version of netstart doesn't need this at 
> > > > > > > all;
> > > > > > > neither does it have the selective semantic nor does vifscreate() 
> > > > > > > exist.
> > > > > > 
> > > > > > Anyone?
> > > > > > 
> > > > > > It seems only logical to treat subsets of interfaces the same way as
> > > > > > a full `sh /etc/netstart'.
> > > > > > 
> > > > > > A pair of pair(4) is one example, I'm certain there are more 
> > > > > > scenarios
> > > > > > where you craft interfaces with `ifconfig ...' in the shell, then 
> > > > > > set up
> > > > > > the hostname.* files and test them with `sh /etc/netstart bridge0 
> > > > > > ...'
> > > > > > where pseudo interfaces are involved.
> > > > > 
> > > > > Anyone?
> > > > > 
> > > > > This is really practical and fixes things at least for me when I 
> > > > > destroy
> > > > > interfaces, reconfigure and recreate them together, for example like 
> > > > > so:
> > > > > 
> > > > >       # ifconfig pair2 destroy
> > > > >       # ifconfig pair1 destroy
> > > > >       ... edit hostname.*
> > > > >       # sh /etc/netstart pair1 pair2
> > > > >       ifconfig: patch pair2: No such file or directory
> > > > >       add net default: gateway 192.0.0.1
> > > > > 
> > > > > (redoing it because who knows what failed due to the order problem and
> > > > > what didn't...)
> > > > > 
> > > > >       # ifconfig pair2 destroy
> > > > >       # ifconfig pair1 destroy
> > > > >       # sh /usr/src/etc/netstart pair1 pair2
> > > > >       add net default: gateway 192.0.0.1
> > > > > 
> > > > > Feedback? Objection? OK?
> > > > 
> > > > One last ping with the same diff on top of -CURRENT.
> > > > 
> > > > 
> > > > Index: etc/netstart
> > > > ===================================================================
> > > > RCS file: /cvs/src/etc/netstart,v
> > > > retrieving revision 1.218
> > > > diff -u -p -r1.218 netstart
> > > > --- etc/netstart        26 Jun 2022 09:36:13 -0000      1.218
> > > > +++ etc/netstart        30 Jun 2022 14:48:46 -0000
> > > > @@ -94,9 +94,11 @@ ifcreate() {
> > > >  }
> > > >  
> > > >  # Create interfaces for network pseudo-devices referred to by 
> > > > hostname.if files.
> > > > -# Usage: vifscreate
> > > > +# Optionally, limit creation to given interfaces only.
> > > > +# Usage: vifscreate [if ...]
> > > >  vifscreate() {
> > > > -       local _vif _hn _if
> > > > +       local _vif _hn _if _ifs
> > > > +       set -A _ifs -- "$@"
> > > >  
> > > >         for _vif in $(ifconfig -C); do
> > > >                 for _hn in /etc/hostname.${_vif}+([[:digit:]]); do
> > > > @@ -106,6 +108,9 @@ vifscreate() {
> > > >                         # loopback for routing domain is created by 
> > > > kernel
> > > >                         [[ -n ${_if##lo[1-9]*} ]] || continue
> > > >  
> > > > +                       ((${#_ifs[*]} > 0)) && [[ ${_ifs[*]} != 
> > > > *${_if}* ]] &&
> > > > +                               continue
> > > 
> > > My gut feeling says this is wrong.
> > > I suspect `netstart vlan0` will create an0.
> > 
> > Sorry, I don't follow;  how would it chop leading chars?
> > 
> > Maybe you meant that somehow `sh /etc/netstart an0' would attempt
> > creating an0 since *an0* would match e.g. "lo0 em0 vlan0" or so?
> 
> Yeah, along those lines, but my example was bogus as $_if would never
> be "an0" here.

Correct.

> However, `sh /etc/vetstart egre0 # (or even whatevergre0)` would create
> gre0 if /etc/hostname.gre0 existed.
> 
> Unless I'm mistaken again. Either way, it feels fragile.

I think you're mistaken, but we're not going that direction anyway.

> > If so, my diff would change nothing in this regard and netstart already
> > behaves... off for bogus interfaces, with and without my diff:
> > 
> >     # echo up > /etc/hostname.vlan0
> >     # echo up > /etc/hostname.an0  
> >     # sh /etc/netstart -n an0 ; echo $?
> >     WARNING: /etc/hostname.an0 is insecure, fixing permissions.
> >     { ifconfig an0 || ifconfig an0 create; }
> >     ifconfig an0 up
> >     0
> >     # sh /etc/netstart an0 ; echo $?
> >     0
> > 
> > > You could probably do
> > > 
> > >   ((${#_ifs[*]} > 0)) && [[ " ${_ifs[*]} " != *" ${_if} "* ]] &&
> > > 
> > > but then it starts looking even worse. :-P
> > 
> > Agreed.
> > 
> > Here's a better diff at the end reusing isin() from install.sub.
> > This way the purely theoretical false-positive globbing goes away and
> > cryptic ksh code is traded for known, mnemonic ksh code.
> > 
> > > > +
> > > >                         if ! ifcreate $_if; then
> > > >                                 print -u2 "${0##*/}: create for '$_if' 
> > > > failed."
> > > >                         fi
> > > > @@ -314,6 +319,7 @@ $PRINT_ONLY || [[ ! -f /etc/soii.key ]] 
> > > >  # If we were invoked with a list of interface names, just reconfigure 
> > > > these
> > > >  # interfaces (or bridges), add default routes and return.
> > > >  if (($# > 0)); then
> > > > +       vifscreate "$@"
> > > >         for _if; do ifstart $_if; done
> > > >         defaultroute
> > > >         return
> > > 
> > > Would it be a problem just creating all pinpointed interfaces, be they
> > > virtual or not, upfront?
> > 
> > That'll fix my use case but I'd need more time to reason about this
> > approach.
> > 
> > ifstart()'s creation comes to mind:
> > 
> > 139         Check for ifconfig'able interface, except if -n option is 
> > specified.
> > 140         ifcreate $_if || return
> > 
> > Will that trip or can it go?
> 
> Given its
> 
>               { ifconfig $_if || ifconfig $_if create; } >/dev/null 2>&1
> 
> , I'd say it's safe.
> 
> > With your diff it'd be purely redundant.
> 
> It would only be redundant for the case when you pass specific interface
> names to netstart.
> 
> However, my loop would attempt to create any specified interface, regardless
> of whether a matching hostname.if file existed...

Oh and it would attempt to create physical interfaces multiple times,
which is an obvious bug or rather redundancy.

-CURRENT netstart does that as well, simply as part of ifstart():

        # sh /etc/netstart -n em0 lo0
        { ifconfig em0 || ifconfig em0 create; }
        ifconfig em0 up
        netstart: /etc/hostname.lo0: No such file or directory.

But your diff does it more than once for physical interfaces and also
does it for loopback devices which are skipped properly in vifscreate():

        # sh /etc/netstart -n em0 lo0
        { ifconfig em0 || ifconfig em0 create; }
        { ifconfig lo0 || ifconfig lo0 create; }
        { ifconfig em0 || ifconfig em0 create; }
        ifconfig em0 up
        netstart: /etc/hostname.lo0: No such file or directory.

> > > diff --git a/etc/netstart b/etc/netstart
> > > index 33e9689a819..62ca64803d8 100644
> > > --- a/etc/netstart
> > > +++ b/etc/netstart
> > > @@ -314,6 +314,7 @@ $PRINT_ONLY || [[ ! -f /etc/soii.key ]] ||
> > >  # If we were invoked with a list of interface names, just reconfigure 
> > > these
> > >  # interfaces (or bridges), add default routes and return.
> > >  if (($# > 0)); then
> > > + for _if; do ifcreate $_if; done
> > >   for _if; do ifstart $_if; done
> > >   defaultroute
> > >   return

So I'd disregard your approach.

> > 
> > 
> > Index: netstart
> > ===================================================================
> > RCS file: /cvs/src/etc/netstart,v
> > retrieving revision 1.218
> > diff -u -p -r1.218 netstart
> > --- netstart        26 Jun 2022 09:36:13 -0000      1.218
> > +++ netstart        2 Jul 2022 20:01:16 -0000
> > @@ -11,6 +11,17 @@ usage() {
> >     exit 1
> >  }
> >  
> > +# Test the first argument against the remaining ones, return success on a 
> > match.
> > +isin() {
> > +   local _a=$1 _b
> > +
> > +   shift
> > +   for _b; do
> > +           [[ $_a == "$_b" ]] && return 0
> > +   done
> > +   return 1
> > +}
> > +
> >  # Echo file $1 to stdout. Skip comment lines. Strip leading and trailing
> >  # whitespace if IFS is set.
> >  # Usage: stripcom /path/to/file
> > @@ -94,9 +105,11 @@ ifcreate() {
> >  }
> >  
> >  # Create interfaces for network pseudo-devices referred to by hostname.if 
> > files.
> > -# Usage: vifscreate
> > +# Optionally, limit creation to given interfaces only.
> > +# Usage: vifscreate [if ...]
> >  vifscreate() {
> > -   local _vif _hn _if
> > +   local _vif _hn _if _ifs
> > +   set -A _ifs -- "$@"
> >  
> >     for _vif in $(ifconfig -C); do
> >             for _hn in /etc/hostname.${_vif}+([[:digit:]]); do
> > @@ -106,6 +119,10 @@ vifscreate() {
> >                     # loopback for routing domain is created by kernel
> >                     [[ -n ${_if##lo[1-9]*} ]] || continue
> >  
> > +                   if ((${#_ifs[*]} > 0)) && ! isin $_if "${_ifs[@]}"; then
> > +                           continue
> > +                   fi
> > +
> 
> Ok, I concur, and isin() is way cleaner. I do think the _ifs dance looks
> messy though, and would suggest just using $@ as-is;

Fine with me and actually better, thanks.

_ifs seems like idiomatic way in our shell code, i.e. assign function
arguments to local variables, but $@ is a bit special.

In fact, "$@" is `set -u' clean whereas "${_ifs[@]}" is not.
netstart does not use `set -u', but it's still an argument for the $@:

        $ set -u
        $ set -A _ifs -- "$@"
        $ echo "$@"

        $ echo "${_ifs[@]}"
        ksh: _ifs[@]: parameter not set


This works like a charm for me and behaviour only differs if I actually
pass interfaces:

        # sh /etc/netstart -n > old
        # sh /usr/src/etc/netstart -n > new
        # diff -u old new ; echo $?
        0

        # sh /etc/netstart -n pair1 pair2 > old-ifs
        # sh /usr/src/etc/netstart -n pair1 pair2 > new-ifs
        # diff -u old-ifs new-ifs
        --- old-ifs     Sun Jul  3 13:54:51 2022
        +++ new-ifs     Sun Jul  3 13:54:45 2022
        @@ -1,4 +1,6 @@
         { ifconfig pair1 || ifconfig pair1 create; }
        +{ ifconfig pair2 || ifconfig pair2 create; }
        +{ ifconfig pair1 || ifconfig pair1 create; }
         ifconfig pair1 inet 192.0.0.4/29
         ifconfig pair1 patch pair2
         { ifconfig pair2 || ifconfig pair2 create; }


Feedback? Objection? OK?


Index: netstart
===================================================================
RCS file: /cvs/src/etc/netstart,v
retrieving revision 1.218
diff -u -p -r1.218 netstart
--- netstart    26 Jun 2022 09:36:13 -0000      1.218
+++ netstart    3 Jul 2022 09:46:05 -0000
@@ -11,6 +11,17 @@ usage() {
        exit 1
 }
 
+# Test the first argument against the remaining ones, return success on a 
match.
+isin() {
+       local _a=$1 _b
+
+       shift
+       for _b; do
+               [[ $_a == "$_b" ]] && return 0
+       done
+       return 1
+}
+
 # Echo file $1 to stdout. Skip comment lines. Strip leading and trailing
 # whitespace if IFS is set.
 # Usage: stripcom /path/to/file
@@ -94,7 +105,8 @@ ifcreate() {
 }
 
 # Create interfaces for network pseudo-devices referred to by hostname.if 
files.
-# Usage: vifscreate
+# Optionally, limit creation to given interfaces only.
+# Usage: vifscreate [if ...]
 vifscreate() {
        local _vif _hn _if
 
@@ -106,6 +118,10 @@ vifscreate() {
                        # loopback for routing domain is created by kernel
                        [[ -n ${_if##lo[1-9]*} ]] || continue
 
+                       if (($# > 0)) && ! isin $_if "$@"; then
+                               continue
+                       fi
+
                        if ! ifcreate $_if; then
                                print -u2 "${0##*/}: create for '$_if' failed."
                        fi
@@ -313,7 +329,11 @@ $PRINT_ONLY || [[ ! -f /etc/soii.key ]] 
 
 # If we were invoked with a list of interface names, just reconfigure these
 # interfaces (or bridges), add default routes and return.
+# Create virtual interfaces upfront to make ifconfig commands depending on
+# other interfaces, e.g. "patch", work regardless of in which order interface
+# names were specified.
 if (($# > 0)); then
+       vifscreate "$@"
        for _if; do ifstart $_if; done
        defaultroute
        return

Reply via email to