#433568 add vlan support

Package:
netcfg
Source:
netcfg
Submitter:
Luca Filipozzi
Date:
2025-04-09 17:03:01 UTC
Severity:
wishlist
Tags:
#433568#5
Date:
2007-07-17 23:53:38 UTC
From:
To:
Please consider adding vlan support to debian-installer.  There are
cases (edge cases, admitedly) where machines and switches have been
configured to use vlan tagging.  A reinstall of these machines requires
a network reconfiguration.  vlan support in the installer would avoid
this reconfiguration.  Thanks for reading this far.

#433568#12
Date:
2007-07-18 17:15:29 UTC
From:
To:
Op 17-07-2007 om 16:53 schreef Luca Filipozzi:

meanwhile reassigned to netcfg

Where to find more information about vlan support?
(Please provide pointer to vlan support in debian)


Cheers
Geert Stappers

#433568#19
Date:
2007-07-18 21:56:55 UTC
From:
To:
 * full support with the debconf questions and proper scripts,
 * only make the necessary udebs available and a good documentation on
   how to enable vlans through a shell.

In my opinion, people who would like to enable VLAN should be pretty
used to the command-line (network switches are usually configured that
way), so the later would be enough, IMHO.

Please look into the vlan [1] package for the necessary command line
tools.  Kernel support is provided by the 8021q module, available in
standard Debian kernel.

[1] http://packages.debian.org/vlan

Once the vlan package is installed, I just need to write something like:

  iface eth0.42 inet dhcp

This will configure an interface named "eth0.42", using the physical
link connected on "eth0" and tagging packets with VLANID 42.

So in any case, we would need to recognize if such interface is used
during the installation process to install the "vlan" package in the
target.

Cheers,

#433568#24
Date:
2008-10-24 11:25:59 UTC
From:
To:
Hi,

I would really love to see vlan support in DI.

I just had a box with vlans connected already and wanted to install
debian with latest lenny installer but couldn't because of the lack of
vlan support.

I'd prefer to have proper deb-conf vlan support, if possible. Shouldn't
be too hard.

Probably you only need to say which interface and which vlan, add this
interface and then start the network dlg.

Thanks
Philipp Kolmann

#433568#29
Date:
2010-01-08 18:57:26 UTC
From:
To:
I too have been bitten by this. For as flexible as the Debian Installer is
and everything you can do, it's pretty disappointing that it can't do VLANs.
A lot of enterprises use VLANs, and it seems it would be important to have
VLAN support to install Debian. It's no fun to reconfigure the network just
to install/reinstall.

Thanks,

Robert LeBlanc
Life Sciences & Undergraduate Education Computer Support
Brigham Young University

#433568#34
Date:
2010-01-09 12:37:50 UTC
From:
To:
Robert LeBlanc <robert@leblancnet.us> writes:

I hacked VLAN support into the Sarge installer once upon a time.  It
wasn't a big deal, like adding the vconfig binary and extending netcfg
here and there a little bit.  The problem is that you can't just
download it like the other optional components, so it would waste memory
in the vast majority of installations.  Which wouldn't be a problem but
in the more memory constrained situations...  I can't judge this.

It would be less of a problem if d-i used the Busybox ip applet *and* if
that supported VLANs.  Until then, I'd work around this by adding the
vconfig binary to the installer medium or loading it as "additional
firmware".  :)

#433568#39
Date:
2010-01-09 13:04:51 UTC
From:
To:
It does (and nothing else).

That might be an issue. No idea.

#433568#44
Date:
2010-02-25 16:26:40 UTC
From:
To:
Hi,

~ # ip link add link eth0 name vlan4 type vlan id 4
ip: invalid argument 'add' to 'ip'

It seems it doesn't support it.

I was bitten by that too yesterday, and would really appreciate if d-i
could support this. According to busybox's manpage it should support
vconfig if enabled, and d-i seems to miss the vlan kernel module for it.

Cheers
Martin

#433568#51
Date:
2012-09-09 13:56:25 UTC
From:
To:
I'm sending a reminder on this issue, it would be best if d-i can
support VLAN using expert mode of installation, configuring VLAN using
iproute2 during installation is much more annoying comparing to
configure it within d-i.

#433568#56
Date:
2012-09-09 15:47:47 UTC
From:
To:
Hi Aron

Aron Xu <happyaron.xu@gmail.com> writes:

A reminder is nice and if you are luck might get things started, but
as you probably know, d-i is free software. Pachtes are most welcome.
Everyone is free to contribute and I don't know of any technically sound
contribution to d-i that has been turned down.

The code is at git.debian.org, the components you probably need to
modify are netcfg and busybox (to enable VLAN support).

Gaudenz

#433568#61
Date:
2012-09-09 15:56:26 UTC
From:
To:
Hi,

Thanks, I know that, and I have been a Debian contributor for quite
some time, :-)

IIRC busybox-udeb has enabled VLAN support, the only thing left is
netcfg. But to be honest I'm not familiar with those stuff...

#433568#68
Date:
2012-10-03 13:03:02 UTC
From:
To:
I patched vlan to work with VLAN for linux and kfreebsd now.
While I have no idea whether we should load 8021q module manually.

I seems that 8021q module is not included in d-i (Linux).

Thanks for Aron Xu to write the templates.

#433568#75
Date:
2012-12-28 19:41:33 UTC
From:
To:
Hi,

The Linux kernel's vlan udeb is added in linux/3.2.35-1, so there is
no outside thing to block us from adding vlan support to netcfg, can
anyone review the patch?

#433568#78
Date:
2012-12-28 20:37:50 UTC
From:
To:
It needs a rewrite, IMHO. I started reviewing / rewriting it, but I'm not
stopping others to work on it. (More debugging output, more sane memory
handling, coding style adjustments.)

I'm not sure I get to it before the next rc, though. At least, if it
works, it could probably also be backported to wheezy's d-i if it's too
late. It's pretty self-contained.

Kind regards
Philipp Kern

#433568#83
Date:
2012-12-29 07:34:27 UTC
From:
To:
Quoting Philipp Kern (pkern@debian.org):

Taking over KiBi's hat, I would say there is no chance this makes it
to wheezy. And taking mine, too, with several addition to localization
bits. The debconf templates need minor edits to compleltely fit with
the writing style we're using elsewhere...and they add 8
strings....and we have 56 complete translations (more than we *ever*
had).

In my opinion, this should go into a jessie branch, in netcfg.

#433568#86
Date:
2012-12-29 13:15:45 UTC
From:
To:
I'd say that this is something that must be preseeded and if anything it
would show errors in English. So I'm not too concerned about that. But if
we agree that it is "useful" to show netcfg/use_vlan in jessie in expert
mode, we can do that too.

Kind regards
Philipp Kern

#433568#91
Date:
2013-01-03 21:42:12 UTC
From:
To:
Thank you for working on this, and sorry for my not able to provide more help.

I don't expect it to be included to Wheezy in current stage of freeze,
and a backport will work. I work part-time in an IDC and the needs of
some not-so-common usages (like vlan support in d-i) are obvious to
me. Replacing some of RHEL/CentOS installations with Debian helps
quite a log in daily operation, but this gives me some chances to find
problems and help on improving Debian in such environments.

It would be handy if the option is shown if vlan udeb is selected when
choosing additional functions during installation, but I doubt whether
this can apply for kBSD as they seem to have vlan support built-in to
the kernel. Also hurd does not support 8021q vlan yet, showing the
option does not make sense for it.

#433568#96
Date:
2013-01-28 01:48:56 UTC
From:
To:
Hello,

(putting back Luca in the loop just in case)

Philipp Kern <pkern@debian.org> (29/12/2012):

I might have considered something “already-suitable” for inclusion
into rc2 or something, but since a rewrite is suggested by Philipp, it
looks like the backport solution looks the way to go (at least Aron is
OK with Philipp's proposal).

ACK.

Mraw,
KiBi.

#433568#101
Date:
2013-03-27 14:10:51 UTC
From:
To:
Thanks for Zhengpeng Hou, who points out that I leave a piece of code which is
not used anymore in this patch. This piece of code make a more malloc while not
free.

And I refresh this patch with the current git.
This is the new patch.

Any one has progress on rewriting it?

#433568#108
Date:
2014-10-29 09:31:00 UTC
From:
To:
Hi,

We're really interested in this, where should we look for a status
update? I still do not see an option for it in the installer for Wheezy,
so I guess it's not done yet. Will it be present for Jessie? Anything we
can do to help?

#433568#113
Date:
2014-10-29 10:06:45 UTC
From:
To:
Tim Stoop <tim@kumina.nl> (2014-10-29):

Philipp, any news on your side?

Mraw,
KiBi.

#433568#118
Date:
2016-03-10 17:11:49 UTC
From:
To:
Hello,


OMG, I just had to go through vlan pain, and *anything* would be
better than current situation, rewrite or not.

One thing I see missing is that apart from writing out correct
/etc/network/interface one also needs to install "vlan" package in the
target installation. Is this done somewhere?

Apart from that nit-pick, this is amazing, and I'm feeling like just
finally merging this.

Regards,

Dimitri.

#433568#123
Date:
2016-03-12 12:49:57 UTC
From:
To:
You don't need vlan; iproute2's ip can do it:

ip link add link eth0 name vlan9 type vlan id 9
or
ip link add link eth0 name eth0.9 type vlan id 9

#433568#128
Date:
2016-03-30 14:11:03 UTC
From:
To:
Hi,

I know that. vlan package is needed for the ifupdown hooks to parse
vlan stanza from /etc/network/interfaces and hence configure vlan id.
Or am I missing something?

Regards,

Dimitri.

#433568#133
Date:
2016-03-30 14:26:52 UTC
From:
To:
Currently the vlan package provides such hooks.  No reason one could
not add such hooks to the iproute2 package or even extend ifupdown to
handle it itself.  Using the vlan package is certainly not efficient
compared to using the ip command for the job.  This is also true for
bridge-utils as far as I know, where I think iproute2 can also completely
replace that.

#433568#140
Date:
2016-03-30 16:18:11 UTC
From:
To:
---
 Makefile                       |  2 +-
 debian/changelog               |  3 ++
 debian/netcfg-common.templates | 34 +++++++++++++++++++--
 netcfg.c                       |  5 ++++
 netcfg.h                       |  2 ++
 vlan.c                         | 67 ++++++++++++++++++++++++++++++++++++++++++
 write_interface.c              | 20 ++++++++++++-
 7 files changed, 129 insertions(+), 4 deletions(-)
 create mode 100644 vlan.c

diff --git a/Makefile b/Makefile
index a15d476..03343c9 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ TARGETS		?= netcfg-static netcfg

 LDOPTS		= -ldebconfclient -ldebian-installer
 CFLAGS		= -W -Wall -Werror -DNDEBUG -DNETCFG_VERSION="\"$(NETCFG_VERSION)\"" -I.
-COMMON_OBJS	= netcfg-common.o wireless.o write_interface.o ipv6.o
+COMMON_OBJS	= netcfg-common.o wireless.o write_interface.o ipv6.o vlan.o
 NETCFG_O   	= netcfg.o dhcp.o static.o ethtool-lite.o wpa.o wpa_ctrl.o rdnssd.o autoconfig.o
 NETCFG_STATIC_O	= netcfg-static.o static.o ethtool-lite.o

diff --git a/debian/changelog b/debian/changelog
index 0930928..2a5e4e6 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -5,6 +5,9 @@ netcfg (1.138) UNRELEASED; urgency=medium
   * static: trim user-specified values for IP and other addresses
   * Closes: #818611, LP: #1541955

+  [ YunQiang Su ]
+  * Add vlan support. Closes: #433568
+
   [ Dimitri John Ledkov ]
   * dhcp.c: check return result of two more fgets calls
   * nm-conf.c: check return result of fscanf
diff --git a/debian/netcfg-common.templates b/debian/netcfg-common.templates
index 2b77936..0dc3446 100644
--- a/debian/netcfg-common.templates
+++ b/debian/netcfg-common.templates
@@ -22,10 +22,41 @@ Type: string
 # :sl1:
 _Description: Domain name:
  The domain name is the part of your Internet address to the right of your
- host name.  It is often something that ends in .com, .net, .edu, or .org.
+ host name.  It is often something that ends in .com, .net, .edu, or .org.
  If you are setting up a home network, you can make something up, but make
  sure you use the same domain name on all your computers.

+Template: netcfg/use_vlan
+Type: boolean
+Default: false
+# :sl6:
+_Description: Are you configuring on an IEEE 802.1Q VLAN trunk port?
+ Virtual LAN (VLAN) is a concept of partitioning a physical network to create
+ distinct broadcast domains. Packets can be marked for different IDs by
+ tagging, so that a single interconnect (trunk) may be used to transport
+ data for various VLANs.
+ .
+ If your network interface is directly attached to a VLAN trunk port,
+ specifying a VLAN ID may be necessary to get a working connection.
+
+Template: netcfg/vlan_id
+Type: string
+# :sl6:
+_Description: VLAN ID (1-4094):
+ VLAN IDs are divided into a normal range and an extended range:
+ .
+ Normal range IDs are 1-1005. 1 is the default native VLAN,
+ and 1002-1005 are reserved for Token Ring and FDDI VLANs.
+ Extended range IDs are 1006-4094, which are designed for service
+ providers and have fewer options.
+
+Template: netcfg/vlan_cmderror
+Type: error
+# :sl6:
+_Description: Error setting VLAN
+ The command used to set VLAN during installation got an error,
+ please go back and try again.
+
 Template: netcfg/get_nameservers
 Type: string
 # :sl1:
@@ -371,4 +402,3 @@ _Choices: ${essid_list} Enter ESSID manually
 # :sl1:
 _Description: Wireless network:
  Select the wireless network to use during the installation process.
-
diff --git a/netcfg.c b/netcfg.c
index 195681b..df0bc04 100644
--- a/netcfg.c
+++ b/netcfg.c
@@ -222,6 +222,11 @@ int main(int argc, char *argv[])
                 else
                     state = GET_METHOD;
             }
+
+            if(netcfg_set_vlan(&interface, client) == GO_BACK){
+                state = BACKUP;
+            }
+
             break;
         case GET_HOSTNAME_ONLY:
             if(netcfg_get_hostname(client, "netcfg/get_hostname", hostname, 0))
diff --git a/netcfg.h b/netcfg.h
index 00a2cea..4dfbdee 100644
--- a/netcfg.h
+++ b/netcfg.h
@@ -270,4 +270,6 @@ extern void cleanup_dhcpv6_client(void);
 extern int start_dhcpv6_client(struct debconfclient *client, const struct netcfg_interface *interface);
 extern int netcfg_autoconfig(struct debconfclient *client, struct netcfg_interface *interface);

+extern int netcfg_set_vlan(struct netcfg_interface *interface, struct debconfclient *client);
+
 #endif /* _NETCFG_H_ */
diff --git a/vlan.c b/vlan.c
new file mode 100644
index 0000000..ec2a19b
--- /dev/null
+++ b/vlan.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <cdebconf/debconfclient.h>
+#include <debian-installer.h>
+#include "netcfg.h"
+
+#define VLAN_SUCESSED 0
+#define VLAN_FAILED 1
+int netcfg_set_vlan(struct netcfg_interface *interface, struct debconfclient *client){
+    char *vlaniface = NULL, *vlanid = NULL, *vlancmd = NULL;
+    int vlaniface_len, vlancmd_len;
+
+/*kfreebsd or hurd has different cmd to set vlan*/
+#if defined(__linux__)
+    char vlancmd_template[] = "ip link add link %s name %s type vlan id %s";
+#elif defined(__FreeBSD_kernel__)
+    /*export 2 more to make it the same formt with Linux*/
+    char vlancmd_tmplate[] = "export NIC=%s; export VNIC=%s; export VLANID=%s;"
+                             " ifconfig $VNIC create";
+#endif
+    int vlancmd_template_len = sizeof(vlancmd_template);
+
+    debconf_input(client, "medium", "netcfg/use_vlan");
+
+    if (debconf_go(client) == CMD_GOBACK)
+       return GO_BACK;
+    debconf_get(client, "netcfg/use_vlan");
+
+    if (!strcmp(client->value, "false")){
+       goto error;
+    }
+
+    debconf_input(client, "critical", "netcfg/vlan_id");
+    debconf_get(client, "netcfg/vlan_id");
+    vlanid = client -> value;
+
+    vlaniface_len = strlen(interface->name)+strlen(vlanid)+2;
+    vlaniface = malloc(vlaniface_len);
+    if(! vlaniface){
+       goto error;
+    }
+    snprintf(vlaniface, vlaniface_len, "%s.%s", interface->name, vlanid);
+    vlancmd_len = vlancmd_template_len + vlaniface_len*2 +1;
+    vlancmd = malloc(vlancmd_len);
+    if(! vlancmd){
+       goto error;
+    }
+    snprintf(vlancmd, vlancmd_len, vlancmd_template, interface->name, vlaniface, vlanid);
+    if(di_exec_shell_log(vlancmd)){
+       di_warning("^ Setting VLAN error: the command is \n%s", vlancmd);
+       debconf_capb(client);
+       debconf_input(client, "critical", "netcfg/vlan_cmderror");
+       debconf_go(client);
+       debconf_capb(client, "backup");
+       goto error;
+    }
+    if(interface->name){
+         free(interface->name);
+         interface->name = vlaniface;
+    }
+    free(vlancmd);
+    return VLAN_SUCESSED;
+
+error:
+    if(vlaniface) free(vlaniface);
+    if(vlancmd) free(vlancmd);
+    return VLAN_FAILED;
+}
diff --git a/write_interface.c b/write_interface.c
index 2ab1a34..be0d78b 100644
--- a/write_interface.c
+++ b/write_interface.c
@@ -44,6 +44,21 @@ static int nc_wi_loopback(const struct netcfg_interface *interface, FILE *fd)
 	return 1;
 }

+/* Write VLAN settings, such as: vlan_raw_device eth0
+*/
+static int nc_wi_vlan(const struct netcfg_interface *interface, FILE *fd)
+{
+    char *dup_name, *strip_name;
+    dup_name = strdup(interface->name);
+    strip_name = strsep(&dup_name, ".");
+    if(strip_name != NULL){
+        fprintf(fd, "\tvlan_raw_device %s\n", strip_name);
+	}
+    free(dup_name);
+	return 1;
+}
+
+
 static int nc_wi_wireless_options(const struct netcfg_interface *interface, FILE *fd)
 {
 	/*
@@ -271,7 +286,10 @@ int netcfg_write_interface(const struct netcfg_interface *interface)
 		di_debug("Writing static IPv6 stanza for %s", interface->name);
 		rv = nc_wi_static_ipv6(interface, fd);
 	}
-
+	if (rv && strchr(interface->name, '.')){
+		di_debug("Writing VLAN: %s", interface->name);
+		rv = nc_wi_vlan(interface, fd);
+	}
 	if (rv && interface && is_wireless_iface(interface->name)) {
 		di_debug("Writing wireless options for %s", interface->name);
 		rv = nc_wi_wireless_options(interface, fd);

#433568#145
Date:
2016-03-30 16:18:12 UTC
From:
To:
---
 debian/changelog  |  2 ++
 netcfg-common.c   |  1 +
 netcfg.h          |  3 +++
 vlan.c            | 16 ++++++++--------
 write_interface.c | 15 +++++++--------
 5 files changed, 21 insertions(+), 16 deletions(-)

diff --git a/debian/changelog b/debian/changelog
index 2a5e4e6..7b7aebe 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -13,6 +13,8 @@ netcfg (1.138) UNRELEASED; urgency=medium
   * nm-conf.c: check return result of fscanf
   * Makefile: link with -lm to resolve undefined log and other functions
   * All of above resolves FTBFS
+  * Impove vlan support, by not relying on '.' to detect and write out
+    vlan raw device stanza

#433568#150
Date:
2016-03-30 16:18:13 UTC
From:
To:
---
 debian/changelog | 1 +
 vlan.c           | 1 +
 2 files changed, 2 insertions(+)

diff --git a/debian/changelog b/debian/changelog
index 7b7aebe..941c16d 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -15,6 +15,7 @@ netcfg (1.138) UNRELEASED; urgency=medium
   * All of above resolves FTBFS
   * Impove vlan support, by not relying on '.' to detect and write out
     vlan raw device stanza
+  * Queue installation of vlan package, at the end of vlan activation

#433568#155
Date:
2016-03-30 19:35:38 UTC
From:
To:
I agree with this.  Debian-installer should not create a network
configuration that depends on tools that have been deprecated for over
a decade.

Ben.

#433568#158
Date:
2016-03-30 23:19:27 UTC
From:
To:
If only I had had a test environment to test my rewrite against, oh
well.

s/configuring on/connected to/? It'd be good to also get the template
changes reviewed.

This description is probably not very useful.

s/got/returned/ I guess.

Please adjust to the surrounding coding style (either no curly braces or
a space before it). I would also have preferred a GET_VLAN state in the
state machine.

SUCESSED?

I would've prefered a get_vlan_command(const char* parentif, const char*
vlanif, int vlanid) here. The attached vlan.c (untested) has it.

Please convert this down into a consistent coding style. (Or maybe look
at the attached file. It has been a long time ago so maybe I didn't make
much sense of it either.)

As you already improved in the third patch the device should get its own
struct member.

Kind regards
Philipp Kern

#433568#163
Date:
2016-04-05 21:57:07 UTC
From:
To:
---

 Squished all the patches, and hopefully did the multi-sided merge of
 Yun's, Pkern's, mine and extra debug stuff. This starts to look
 reasonable. Successfully tested this on s390x, and I'm glad I did it
 there, as I had to fix up the s390x specific code path too.

 I do believe it should be possible to interractively set
 vlan. Currently I am asking this question a "medium" priority, but I
 am open to changing to "low". E.g. I find it acceptable that a user
 needs to return to the main menu and change debconf priority.

 Or for example, should we show it in the static-config by default,
 and offer "configure vlan" as an option when automatic network
 detection fails? E.g. alongside the retry-dhcp,
 retry-dhcp-with-hostname, do-wifi, etc. options?

 Preseeding should be trivial - simply preseed netcfg/vlan_id=2654 and
 that's it.

 Updated template texts a bit. Given that the state-machine, templates
 and all of this patch is still under development, I have not sent
 these off to l10n-en for review.

 Any further comments about this update patch?

 Makefile                       |  2 +-
 debian/changelog               |  7 +++
 debian/netcfg-common.templates | 28 +++++++++++-
 dhcp.c                         |  2 +-
 netcfg-common.c                | 32 ++++++++++++--
 netcfg.c                       | 20 ++++++---
 netcfg.h                       | 11 +++++
 static.c                       | 12 +++---
 vlan.c                         | 97 ++++++++++++++++++++++++++++++++++++++++++
 wireless.c                     |  4 +-
 write_interface.c              | 19 ++++++++-
 11 files changed, 213 insertions(+), 21 deletions(-)
 create mode 100644 vlan.c

diff --git a/Makefile b/Makefile
index a15d476..03343c9 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ TARGETS		?= netcfg-static netcfg

 LDOPTS		= -ldebconfclient -ldebian-installer
 CFLAGS		= -W -Wall -Werror -DNDEBUG -DNETCFG_VERSION="\"$(NETCFG_VERSION)\"" -I.
-COMMON_OBJS	= netcfg-common.o wireless.o write_interface.o ipv6.o
+COMMON_OBJS	= netcfg-common.o wireless.o write_interface.o ipv6.o vlan.o
 NETCFG_O   	= netcfg.o dhcp.o static.o ethtool-lite.o wpa.o wpa_ctrl.o rdnssd.o autoconfig.o
 NETCFG_STATIC_O	= netcfg-static.o static.o ethtool-lite.o

diff --git a/debian/changelog b/debian/changelog
index f3dd40c..c2dae40 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+netcfg (1.139) UNRELEASED; urgency=medium
+
+  * Add vlan support, based on YunQiang Su patch and Phillip Kern's
+    rewrite, with own additions. Closes: #433568
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Mon, 04 Apr 2016 12:18:08 +0100
+
 netcfg (1.138) unstable; urgency=medium

   [ Hendrik Brueckner ]
diff --git a/debian/netcfg-common.templates b/debian/netcfg-common.templates
index 2b77936..bffded9 100644
--- a/debian/netcfg-common.templates
+++ b/debian/netcfg-common.templates
@@ -26,6 +26,33 @@ _Description: Domain name:
  If you are setting up a home network, you can make something up, but make
  sure you use the same domain name on all your computers.

+Template: netcfg/use_vlan
+Type: boolean
+Default: false
+# :sl6:
+_Description: Are you connected to an IEEE 802.1Q VLAN trunk port?
+ Virtual LAN (VLAN) is a concept of partitioning a physical network to create
+ distinct broadcast domains. Packets can be marked for different IDs by
+ tagging, so that a single interconnect (trunk) may be used to transport
+ data for various VLANs.
+ .
+ If your network interface is directly connected to a VLAN trunk port,
+ specifying a VLAN ID may be necessary to get a working connection.
+
+Template: netcfg/vlan_id
+Type: string
+# :sl6:
+_Description: VLAN ID (1-4094):
+ If your network interface is directly connected to a VLAN trunk port,
+ specifying a VLAN ID may be necessary to get a working connection.
+
+Template: netcfg/vlan_failed
+Type: error
+# :sl6:
+_Description: Error setting up VLAN
+ The command used to set up VLAN during the installation returned an
+ error, please go back and try again.
+
 Template: netcfg/get_nameservers
 Type: string
 # :sl1:
@@ -371,4 +398,3 @@ _Choices: ${essid_list} Enter ESSID manually
 # :sl1:
 _Description: Wireless network:
  Select the wireless network to use during the installation process.
-
diff --git a/dhcp.c b/dhcp.c
index fe06950..9476bac 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -459,7 +459,7 @@ int netcfg_activate_dhcp (struct debconfclient *client, struct netcfg_interface
     kill_dhcp_client();
     loop_setup();

-    interface_up(interface->name);
+    netcfg_interface_up(interface);

     for (;;) {
         di_debug("State is now %i", state);
diff --git a/netcfg-common.c b/netcfg-common.c
index c6d1d8d..ac2c6df 100644
--- a/netcfg-common.c
+++ b/netcfg-common.c
@@ -267,7 +267,7 @@ int is_raw_80211(const char *iface)

 #if defined(__s390__)
 // Layer 3 qeth on s390(x) cannot do arping to test gateway reachability.
-int is_layer3_qeth(const char *iface)
+int is_layer3_qeth(const struct netcfg_interface *interface)
 {
     const int bufsize = 1024;
     int retval = 0;
@@ -277,6 +277,12 @@ int is_layer3_qeth(const char *iface)
     ssize_t slen;
     char* driver;
     int fd;
+    char* iface;
+
+    if (interface->parentif)
+        iface = interface->parentif;
+    else
+        iface = interface->name;

     // This is sufficient for both /driver and /layer2.
     len = strlen(SYSCLASSNET) + strlen(iface) + strlen("/device/driver") + 1;
@@ -329,7 +335,7 @@ out:
     return retval;
 }
 #else
-int is_layer3_qeth(const char *iface __attribute__((unused)))
+int is_layer3_qeth(const struct netcfg_interface *interface  __attribute__((unused)))
 {
     return 0;
 }
@@ -1338,6 +1344,24 @@ void interface_down (const char *if_name)
     }
 }

+void netcfg_interface_up (const struct netcfg_interface *iface)
+{
+	if (iface->parentif)
+		interface_up (iface->parentif);
+
+	if (iface->name)
+		interface_up (iface->name);
+}
+
+void netcfg_interface_down (const struct netcfg_interface *iface)
+{
+	if (iface->name)
+		interface_down (iface->name);
+
+	if (iface->parentif)
+		interface_down (iface->parentif);
+}
+
 void parse_args (int argc, char ** argv)
 {
     if (argc == 2) {
@@ -1528,7 +1552,7 @@ int netcfg_detect_link(struct debconfclient *client, const struct netcfg_interfa
         if (ethtool_lite(if_name) == 1) /* ethtool-lite's CONNECTED */ {
             di_info("Found link on %s", if_name);

-            if (!empty_str(gateway) && !is_wireless_iface(if_name) && !is_layer3_qeth(if_name)) {
+            if (!empty_str(gateway) && !is_wireless_iface(if_name) && !is_layer3_qeth(interface)) {
                 for (count = 0; count < gw_tries; count++) {
                     if (di_exec_shell_log(arping) == 0)
                         break;
@@ -1564,6 +1588,8 @@ void netcfg_interface_init(struct netcfg_interface *iface)
     iface->v6_stateless_config = -1;
     iface->loopback = -1;
     iface->mode = MANAGED;
+    iface->parentif = NULL;
+    iface->vlanid = -1;
 }

 /* Parse an IP address (v4 or v6), with optional CIDR netmask, into
diff --git a/netcfg.c b/netcfg.c
index 195681b..c918641 100644
--- a/netcfg.c
+++ b/netcfg.c
@@ -67,6 +67,7 @@ int main(int argc, char *argv[])
            GET_METHOD,
            GET_DHCP,
            GET_STATIC,
+           GET_VLAN,
            WCONFIG,
            WCONFIG_ESSID,
            WCONFIG_SECURITY_TYPE,
@@ -216,13 +217,19 @@ int main(int argc, char *argv[])
                 state = BACKUP;
             else if (! interface.name || ! num_interfaces)
                 state = GET_HOSTNAME_ONLY;
-            else {
-                if (is_wireless_iface (interface.name))
-                    state = WCONFIG;
-                else
-                    state = GET_METHOD;
-            }
+            else if (is_wireless_iface (interface.name))
+                state = WCONFIG;
+            else
+                state = GET_VLAN;
+            break;
+
+	case GET_VLAN:
+            if (netcfg_set_vlan(client, &interface) == GO_BACK)
+                state = BACKUP;
+            else
+                state = GET_METHOD;
             break;
+
         case GET_HOSTNAME_ONLY:
             if(netcfg_get_hostname(client, "netcfg/get_hostname", hostname, 0))
                 state = BACKUP;
@@ -231,6 +238,7 @@ int main(int argc, char *argv[])
                 state = QUIT;
             }
             break;
+
         case GET_METHOD:
             if ((res = netcfg_get_method(client)) == GO_BACK)
                 state = (num_interfaces == 1) ? BACKUP : GET_INTERFACE;
diff --git a/netcfg.h b/netcfg.h
index 00a2cea..8b45776 100644
--- a/netcfg.h
+++ b/netcfg.h
@@ -151,6 +151,11 @@ struct netcfg_interface {
 	/* WPA */
 	wpa_t wpa_supplicant_status;
 	char *passphrase;
+
+	/* VLAN */
+	char *parentif;
+	int vlanid;
+
 };

 /* Somewhere we can store both in_addr and in6_addr; convenient for all those
@@ -221,6 +226,9 @@ extern void deconfigure_network(struct netcfg_interface *iface);
 extern void interface_up (const char *if_name);
 extern void interface_down (const char *if_name);

+extern void netcfg_interface_up (const struct netcfg_interface *iface);
+extern void netcfg_interface_down (const struct netcfg_interface *iface);
+
 extern void loop_setup(void);
 extern int get_hostname_from_dns(const struct netcfg_interface *interface, char *hostname, const size_t max_hostname_len);

@@ -270,4 +278,7 @@ extern void cleanup_dhcpv6_client(void);
 extern int start_dhcpv6_client(struct debconfclient *client, const struct netcfg_interface *interface);
 extern int netcfg_autoconfig(struct debconfclient *client, struct netcfg_interface *interface);

+/* vlan.c */
+extern int netcfg_set_vlan(struct debconfclient *client, struct netcfg_interface *interface);
+
 #endif /* _NETCFG_H_ */
diff --git a/static.c b/static.c
index ea12fba..ba03931 100644
--- a/static.c
+++ b/static.c
@@ -310,7 +310,7 @@ static int netcfg_activate_static_ipv4(struct debconfclient *client,
     deconfigure_network(NULL);

     loop_setup();
-    interface_up(interface->name);
+    netcfg_interface_up(interface);

     /* Flush all previous addresses, routes */
     snprintf(buf, sizeof(buf), "ifconfig %s inet 0 down", interface->name);
@@ -345,7 +345,7 @@ static int netcfg_activate_static_ipv4(struct debconfclient *client,
     deconfigure_network(NULL);

     loop_setup();
-    interface_up(interface->name);
+    netcfg_interface_up(interface);

     /* Flush all previous addresses, routes */
     snprintf(buf, sizeof(buf), "ip -f inet addr flush dev %s", interface->name);
@@ -426,7 +426,7 @@ static int netcfg_activate_static_ipv6(struct debconfclient *client,
     deconfigure_network(NULL);

     loop_setup();
-    interface_up(interface->name);
+    netcfg_interface_up(interface);

     /* Flush all previous addresses, routes */
     snprintf(buf, sizeof(buf), "ifconfig %s inet 0 down", interface->name);
@@ -449,7 +449,7 @@ static int netcfg_activate_static_ipv6(struct debconfclient *client,
     deconfigure_network(NULL);

     loop_setup();
-    interface_up(interface->name);
+    netcfg_interface_up(interface);

     /* Flush all previous addresses, routes */
     snprintf(buf, sizeof(buf), "ip -f inet6 addr flush dev %s", interface->name);
@@ -461,8 +461,8 @@ static int netcfg_activate_static_ipv6(struct debconfclient *client,
     /* Now down and up the interface, to get LL and SLAAC addresses back,
      * since flushing the addresses and routes gets rid of all that
      * sort of thing. */
-    interface_down(interface->name);
-    interface_up(interface->name);
+    netcfg_interface_down(interface);
+    netcfg_interface_up(interface);

     /* Add the new IP address and netmask */
     snprintf(buf, sizeof(buf), "ip addr add %s/%d dev %s",
diff --git a/vlan.c b/vlan.c
new file mode 100644
index 0000000..d3aa227
--- /dev/null
+++ b/vlan.c
@@ -0,0 +1,97 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cdebconf/debconfclient.h>
+#include <debian-installer.h>
+#include "netcfg.h"
+
+static char* get_vlan_command(const char* parentif, const char* vlanif, int vlanid) {
+#if defined(__linux__)
+	const char* vlan_command = "ip link add link %s name %s type vlan id %d";
+	int len = strlen(vlan_command) + strlen(parentif) + strlen(vlanif) + 4 + 1;
+	char* buf = malloc(len);
+	snprintf(buf, len, vlan_command, parentif, vlanif, vlanid);
+	return buf;
+#elif defined(__FreeBSD_kernel__)
+	const char* vlan_command = "ifconfig %s vlan %d vlandev %s";
+	int len = strlen(vlan_command) + strlen(parentif) + strlen(vlanif) + 4 + 1;
+	char* buf = malloc(len);
+	snprintf(buf, len, vlan_command, vlanif, vlanid, parentif);
+	return buf;
+#endif
+}
+
+/* Create a new VLAN interface attached to the currently selected
+ * network interface.
+ */
+int netcfg_set_vlan (struct debconfclient *client, struct netcfg_interface *interface) {
+#if defined(__linux__) || defined(__FreeBSD_kernel__)
+	int vlanid;
+
+	debconf_get(client, "netcfg/vlan_id");
+	/* Empty string: no VLAN preseeded, ask if we should configure VLAN */
+	if (strlen(client->value) == 0) {
+		debconf_input (client, "medium", "netcfg/use_vlan");
+		if (debconf_go(client) == GO_BACK) {
+			return GO_BACK;
+		}
+
+		debconf_get(client, "netcfg/use_vlan");
+
+		if (!strcmp(client->value, "false")) {
+			return 0;
+		}
+
+		debconf_input(client, "critical", "netcfg/vlan_id");
+		if (debconf_go(client) == GO_BACK) {
+			return GO_BACK;
+		}
+		debconf_get(client, "netcfg/vlan_id");
+	}
+
+	for (;;) {
+		vlanid = strtol(client->value, NULL, 10);
+		/* Valid range: 1-4094 (0 and 4095 are reserved.)
+		 * 0 will be returned by strtol if the value cannot be parsed.
+		 */
+		if (vlanid < 1 || vlanid > 4094) {
+			di_error("VLAN ID \"%s\" is invalid.", client->value);
+			debconf_subst(client, "netcfg/vlan_id_invalid", "vlan_id", client->value);
+			debconf_input(client, "critical", "netcfg/invalid_vlan");
+			debconf_capb(client);
+			debconf_go(client);
+
+			debconf_capb(client, "backup");
+			debconf_input(client, "critical", "netcfg/vlan_id");
+			if (debconf_go(client) == GO_BACK) {
+				return GO_BACK;
+			}
+		} else {
+			break;
+		}
+	}
+
+	int vlaniflen = strlen(interface->name) + 1 + 4 + 1;
+	char* vlanif = malloc(vlaniflen);
+	snprintf(vlanif, vlaniflen, "%s.%d", interface->name, vlanid);
+
+	char *vlan_command = get_vlan_command(interface->name, vlanif, vlanid);
+	int rc = di_exec_shell_log(vlan_command);
+	if (rc != 0) {
+		di_error("\"%s\" failed to create VLAN interface; return code = %d", vlan_command, rc);
+		free(vlan_command);
+		debconf_input(client, "critical", "netcfg/vlan_failed");
+		debconf_go(client);
+		return GO_BACK;
+	}
+	free(vlan_command);
+
+	interface->parentif = interface->name;
+	interface->name = vlanif;
+	interface->vlanid = vlanid;
+	di_exec_shell_log("apt-install vlan");
+#else
+	/* This platform does not support VLANs. */
+#endif
+	return 0;
+}
diff --git a/wireless.c b/wireless.c
index 566b032..83b1d1d 100644
--- a/wireless.c
+++ b/wireless.c
@@ -121,7 +121,7 @@ int netcfg_wireless_show_essids(struct debconfclient *client, struct netcfg_inte
     int essid_list_len = 1;

     iw_get_basic_config (wfd, interface->name, &wconf);
-    interface_up(interface->name);
+    netcfg_interface_up(interface);

     if (iw_scan(wfd, interface->name, iw_get_kernel_we_version(),
                 &network_list) >= 0 ) {
@@ -205,7 +205,7 @@ int netcfg_wireless_show_essids(struct debconfclient *client, struct netcfg_inte
     }

     iw_set_basic_config(wfd, interface->name, &wconf);
-    interface_down(interface->name);
+    netcfg_interface_down(interface);

     di_info("Network chosen: %s. Proceeding to connect.", interface->essid);

diff --git a/write_interface.c b/write_interface.c
index 2ab1a34..e768002 100644
--- a/write_interface.c
+++ b/write_interface.c
@@ -44,6 +44,20 @@ static int nc_wi_loopback(const struct netcfg_interface *interface, FILE *fd)
 	return 1;
 }

+/* Write VLAN settings, such as: vlan_raw_device eth0
+*/
+static int nc_wi_vlan(const struct netcfg_interface *interface, FILE *fd)
+{
+	int rv;
+	rv = 1;
+	if (interface && interface->parentif &&
+	    (fprintf(fd, "\tvlan_raw_device %s\n", interface->parentif) < 0)) {
+		rv = 0;
+	}
+	return rv;
+}
+
+
 static int nc_wi_wireless_options(const struct netcfg_interface *interface, FILE *fd)
 {
 	/*
@@ -271,7 +285,10 @@ int netcfg_write_interface(const struct netcfg_interface *interface)
 		di_debug("Writing static IPv6 stanza for %s", interface->name);
 		rv = nc_wi_static_ipv6(interface, fd);
 	}
-
+	if (rv && interface && interface->parentif) {
+		di_debug("Writing VLAN: %s", interface->name);
+		rv = nc_wi_vlan(interface, fd);
+	}
 	if (rv && interface && is_wireless_iface(interface->name)) {
 		di_debug("Writing wireless options for %s", interface->name);
 		rv = nc_wi_wireless_options(interface, fd);

#433568#166
Date:
2016-04-10 17:09:48 UTC
From:
To:
Hi,

That doesn't sound totally crazy I guess.

I'd still like this to be reviewed by them and Christian for sanity,
though. Sublevels, understandability, ... ;-)

Philipp  ;-)

I sort of fear that "go back and try again" would not be helpful. Check
the logs is probably more sensible. \-:

I somewhat feel that it should fail if vlan_id has been set. But on the
other side it also shouldn't prompt for it. Maybe debconf_get it and
return an error so that the error screen is shown and log the concrete
ENOIMPL to syslog?

Would it make sense to have network-manager support as well?  It should
be fairly simple and along these lines:

[vlan]
parent=%parentif%
id=%vlanid%

Otherwise this looks pretty good to me at this point. Much thanks for
doing the effort.

Kind regards
Philipp Kern

#433568#171
Date:
2016-04-15 02:17:00 UTC
From:
To:
---

 Changes since last version:
 - Corrected Philipp's name
 - Adjusted vlan_failed text message
 - Adding a di_error if vlan_id is preseeded and not supported
 - Added NetworkManager vlan type
 - Sent templates for review

 Makefile                       |   2 +-
 debian/changelog               |   7 +++
 debian/netcfg-common.templates |  29 +++++++++++-
 dhcp.c                         |   2 +-
 netcfg-common.c                |  32 +++++++++++--
 netcfg.c                       |  20 +++++---
 netcfg.h                       |  11 +++++
 nm-conf.c                      |  35 +++++++++++++-
 nm-conf.h                      |  10 +++-
 static.c                       |  12 ++---
 vlan.c                         | 101 +++++++++++++++++++++++++++++++++++++++++
 wireless.c                     |   4 +-
 write_interface.c              |  19 +++++++-
 13 files changed, 260 insertions(+), 24 deletions(-)
 create mode 100644 vlan.c

diff --git a/Makefile b/Makefile
index a15d476..03343c9 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ TARGETS		?= netcfg-static netcfg

 LDOPTS		= -ldebconfclient -ldebian-installer
 CFLAGS		= -W -Wall -Werror -DNDEBUG -DNETCFG_VERSION="\"$(NETCFG_VERSION)\"" -I.
-COMMON_OBJS	= netcfg-common.o wireless.o write_interface.o ipv6.o
+COMMON_OBJS	= netcfg-common.o wireless.o write_interface.o ipv6.o vlan.o
 NETCFG_O   	= netcfg.o dhcp.o static.o ethtool-lite.o wpa.o wpa_ctrl.o rdnssd.o autoconfig.o
 NETCFG_STATIC_O	= netcfg-static.o static.o ethtool-lite.o

diff --git a/debian/changelog b/debian/changelog
index f3dd40c..2ed40a2 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+netcfg (1.139) UNRELEASED; urgency=medium
+
+  * Add vlan support, based on YunQiang Su patch and Philipp Kern's
+    rewrite, with own additions. Closes: #433568
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Mon, 04 Apr 2016 12:18:08 +0100
+
 netcfg (1.138) unstable; urgency=medium

   [ Hendrik Brueckner ]
diff --git a/debian/netcfg-common.templates b/debian/netcfg-common.templates
index 2b77936..ebbfbb3 100644
--- a/debian/netcfg-common.templates
+++ b/debian/netcfg-common.templates
@@ -26,6 +26,34 @@ _Description: Domain name:
  If you are setting up a home network, you can make something up, but make
  sure you use the same domain name on all your computers.

+Template: netcfg/use_vlan
+Type: boolean
+Default: false
+# :sl6:
+_Description: Are you connected to an IEEE 802.1Q VLAN trunk port?
+ Virtual LAN (VLAN) is a concept of partitioning a physical network to create
+ distinct broadcast domains. Packets can be marked for different IDs by
+ tagging, so that a single interconnect (trunk) may be used to transport
+ data for various VLANs.
+ .
+ If your network interface is directly connected to a VLAN trunk port,
+ specifying a VLAN ID may be necessary to get a working connection.
+
+Template: netcfg/vlan_id
+Type: string
+# :sl6:
+_Description: VLAN ID (1-4094):
+ If your network interface is directly connected to a VLAN trunk port,
+ specifying a VLAN ID may be necessary to get a working connection.
+
+Template: netcfg/vlan_failed
+Type: error
+# :sl6:
+_Description: Error setting up VLAN
+ The command used to set up VLAN during the installation returned an
+ error, please check installer logs, or go back and try another
+ configuration.
+
 Template: netcfg/get_nameservers
 Type: string
 # :sl1:
@@ -371,4 +399,3 @@ _Choices: ${essid_list} Enter ESSID manually
 # :sl1:
 _Description: Wireless network:
  Select the wireless network to use during the installation process.
-
diff --git a/dhcp.c b/dhcp.c
index fe06950..9476bac 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -459,7 +459,7 @@ int netcfg_activate_dhcp (struct debconfclient *client, struct netcfg_interface
     kill_dhcp_client();
     loop_setup();

-    interface_up(interface->name);
+    netcfg_interface_up(interface);

     for (;;) {
         di_debug("State is now %i", state);
diff --git a/netcfg-common.c b/netcfg-common.c
index c6d1d8d..ac2c6df 100644
--- a/netcfg-common.c
+++ b/netcfg-common.c
@@ -267,7 +267,7 @@ int is_raw_80211(const char *iface)

 #if defined(__s390__)
 // Layer 3 qeth on s390(x) cannot do arping to test gateway reachability.
-int is_layer3_qeth(const char *iface)
+int is_layer3_qeth(const struct netcfg_interface *interface)
 {
     const int bufsize = 1024;
     int retval = 0;
@@ -277,6 +277,12 @@ int is_layer3_qeth(const char *iface)
     ssize_t slen;
     char* driver;
     int fd;
+    char* iface;
+
+    if (interface->parentif)
+        iface = interface->parentif;
+    else
+        iface = interface->name;

     // This is sufficient for both /driver and /layer2.
     len = strlen(SYSCLASSNET) + strlen(iface) + strlen("/device/driver") + 1;
@@ -329,7 +335,7 @@ out:
     return retval;
 }
 #else
-int is_layer3_qeth(const char *iface __attribute__((unused)))
+int is_layer3_qeth(const struct netcfg_interface *interface  __attribute__((unused)))
 {
     return 0;
 }
@@ -1338,6 +1344,24 @@ void interface_down (const char *if_name)
     }
 }

+void netcfg_interface_up (const struct netcfg_interface *iface)
+{
+	if (iface->parentif)
+		interface_up (iface->parentif);
+
+	if (iface->name)
+		interface_up (iface->name);
+}
+
+void netcfg_interface_down (const struct netcfg_interface *iface)
+{
+	if (iface->name)
+		interface_down (iface->name);
+
+	if (iface->parentif)
+		interface_down (iface->parentif);
+}
+
 void parse_args (int argc, char ** argv)
 {
     if (argc == 2) {
@@ -1528,7 +1552,7 @@ int netcfg_detect_link(struct debconfclient *client, const struct netcfg_interfa
         if (ethtool_lite(if_name) == 1) /* ethtool-lite's CONNECTED */ {
             di_info("Found link on %s", if_name);

-            if (!empty_str(gateway) && !is_wireless_iface(if_name) && !is_layer3_qeth(if_name)) {
+            if (!empty_str(gateway) && !is_wireless_iface(if_name) && !is_layer3_qeth(interface)) {
                 for (count = 0; count < gw_tries; count++) {
                     if (di_exec_shell_log(arping) == 0)
                         break;
@@ -1564,6 +1588,8 @@ void netcfg_interface_init(struct netcfg_interface *iface)
     iface->v6_stateless_config = -1;
     iface->loopback = -1;
     iface->mode = MANAGED;
+    iface->parentif = NULL;
+    iface->vlanid = -1;
 }

 /* Parse an IP address (v4 or v6), with optional CIDR netmask, into
diff --git a/netcfg.c b/netcfg.c
index 195681b..c918641 100644
--- a/netcfg.c
+++ b/netcfg.c
@@ -67,6 +67,7 @@ int main(int argc, char *argv[])
            GET_METHOD,
            GET_DHCP,
            GET_STATIC,
+           GET_VLAN,
            WCONFIG,
            WCONFIG_ESSID,
            WCONFIG_SECURITY_TYPE,
@@ -216,13 +217,19 @@ int main(int argc, char *argv[])
                 state = BACKUP;
             else if (! interface.name || ! num_interfaces)
                 state = GET_HOSTNAME_ONLY;
-            else {
-                if (is_wireless_iface (interface.name))
-                    state = WCONFIG;
-                else
-                    state = GET_METHOD;
-            }
+            else if (is_wireless_iface (interface.name))
+                state = WCONFIG;
+            else
+                state = GET_VLAN;
+            break;
+
+	case GET_VLAN:
+            if (netcfg_set_vlan(client, &interface) == GO_BACK)
+                state = BACKUP;
+            else
+                state = GET_METHOD;
             break;
+
         case GET_HOSTNAME_ONLY:
             if(netcfg_get_hostname(client, "netcfg/get_hostname", hostname, 0))
                 state = BACKUP;
@@ -231,6 +238,7 @@ int main(int argc, char *argv[])
                 state = QUIT;
             }
             break;
+
         case GET_METHOD:
             if ((res = netcfg_get_method(client)) == GO_BACK)
                 state = (num_interfaces == 1) ? BACKUP : GET_INTERFACE;
diff --git a/netcfg.h b/netcfg.h
index 00a2cea..8b45776 100644
--- a/netcfg.h
+++ b/netcfg.h
@@ -151,6 +151,11 @@ struct netcfg_interface {
 	/* WPA */
 	wpa_t wpa_supplicant_status;
 	char *passphrase;
+
+	/* VLAN */
+	char *parentif;
+	int vlanid;
+
 };

 /* Somewhere we can store both in_addr and in6_addr; convenient for all those
@@ -221,6 +226,9 @@ extern void deconfigure_network(struct netcfg_interface *iface);
 extern void interface_up (const char *if_name);
 extern void interface_down (const char *if_name);

+extern void netcfg_interface_up (const struct netcfg_interface *iface);
+extern void netcfg_interface_down (const struct netcfg_interface *iface);
+
 extern void loop_setup(void);
 extern int get_hostname_from_dns(const struct netcfg_interface *interface, char *hostname, const size_t max_hostname_len);

@@ -270,4 +278,7 @@ extern void cleanup_dhcpv6_client(void);
 extern int start_dhcpv6_client(struct debconfclient *client, const struct netcfg_interface *interface);
 extern int netcfg_autoconfig(struct debconfclient *client, struct netcfg_interface *interface);

+/* vlan.c */
+extern int netcfg_set_vlan(struct debconfclient *client, struct netcfg_interface *interface);
+
 #endif /* _NETCFG_H_ */
diff --git a/nm-conf.c b/nm-conf.c
index aeab031..66d549b 100644
--- a/nm-conf.c
+++ b/nm-conf.c
@@ -32,11 +32,17 @@ static void get_uuid(char* target)

 static void nm_write_connection(FILE *config_file, nm_connection connection)
 {
+    static char *type = NM_DEFAULT_WIRED;
+    if (connection.type == WIFI) {
+	    type = NM_DEFAULT_WIRELESS;
+    }
+    if (connection.type == VLAN) {
+	    type = NM_DEFAULT_VLAN;
+    }
     fprintf(config_file, "\n%s\n", NM_SETTINGS_CONNECTION);
     fprintf(config_file, "id=%s\n", connection.id);
     fprintf(config_file, "uuid=%s\n", connection.uuid);
-    fprintf(config_file, "type=%s\n", (connection.type == WIFI) ?
-            NM_DEFAULT_WIRELESS : NM_DEFAULT_WIRED);
+    fprintf(config_file, "type=%s\n", type);
 }

 #ifdef WIRELESS
@@ -71,6 +77,15 @@ static void nm_write_wired_specific_options(FILE *config_file,
     }
 }

+static void nm_write_vlan_specific_options(FILE *config_file,
+        struct nm_config_info *nmconf)
+{
+    nm_vlan vlan = nmconf->vlan;
+    fprintf(config_file, "\n%s\n", NM_SETTINGS_VLAN);
+    fprintf(config_file, "parent=%s\n", vlan.parent);
+    fprintf(config_file, "parent=%i\n", vlan.id);
+}
+
 #ifdef WIRELESS
 static void nm_write_wireless_security(FILE *config_file, nm_wireless_security
         wireless_security)
@@ -176,6 +191,9 @@ static void nm_write_connection_type(struct nm_config_info nmconf)
     if (nmconf.connection.type == WIFI) {
         fprintf(f, "connection type: wireless\n");
     }
+    else if (nmconf.connection.type == VLAN) {
+        fprintf(f, "connection type: vlan\n");
+    }
     else {
         fprintf(f, "connection type: wired\n");
     }
@@ -229,6 +247,9 @@ void nm_write_configuration(struct nm_config_info nmconf)
     if (nmconf.connection.type == WIRED) {
         nm_write_wired_specific_options(config_file, &nmconf);
     }
+    else if (nmconf.connection.type == VLAN) {
+        nm_write_vlan_specific_options(config_file, &nmconf);
+    }
 #ifdef WIRELESS
     else {
         nm_write_wireless_specific_options(config_file, &nmconf);
@@ -420,6 +441,15 @@ static void nm_get_ipv6(struct netcfg_interface *niface, nm_ipvX *ipv6)

 }

+static void nm_get_vlan(struct netcfg_interface *niface, nm_connection *connection, nm_vlan *nmvlan)
+{
+    if (niface->vlanid > 0) {
+	    connection->type = VLAN;
+	    nmvlan->id = niface->vlanid;
+	    nmvlan->parent = niface->parentif;
+    }
+}
+
 /* Extract all configs for a wireless interface, from both global netcfg
  * values and other resources. */
 #ifdef WIRELESS
@@ -444,6 +474,7 @@ static void nm_get_wired_config(struct netcfg_interface *niface, struct nm_confi
     nm_get_wired_specific_options(niface, &(nmconf->wired));
     nm_get_ipv4(niface, &(nmconf->ipv4));
     nm_get_ipv6(niface, &(nmconf->ipv6));
+    nm_get_vlan(niface, &(nmconf->connection), &(nmconf->vlan));
 }

 /* Getting configurations for NM relies on netcfrg global variables. */
diff --git a/nm-conf.h b/nm-conf.h
index 6340222..9a6b695 100644
--- a/nm-conf.h
+++ b/nm-conf.h
@@ -20,6 +20,7 @@
 #define NM_DEFAULT_WIRED_NAME           "Wired connection 1"
 #define NM_DEFAULT_WIRELESS             "802-11-wireless"
 #define NM_DEFAULT_WIRELESS_SECURITY    "802-11-wireless-security"
+#define NM_DEFAULT_VLAN                 "vlan"
 #define NM_DEFAULT_PATH_FOR_MAC         "/sys/class/net/%s/address"
 #define NM_CONFIG_FILE_PATH             "/etc/NetworkManager/system-connections"
 #define NM_CONNECTION_FILE              "/tmp/connection_type"
@@ -30,6 +31,7 @@
 #define NM_SETTINGS_WIRELESS_SECURITY   "["NM_DEFAULT_WIRELESS_SECURITY"]"
 #define NM_SETTINGS_IPV4                "[ipv4]"
 #define NM_SETTINGS_IPV6                "[ipv6]"
+#define NM_SETTINGS_VLAN                "[vlan]"

 /* Minimalist structures for storing basic elements in order to write a Network
  * Manager format config file.
@@ -43,7 +45,7 @@ typedef struct nm_connection
 {
     char id[NM_MAX_LEN_ID];
     char uuid[NM_MAX_LEN_UUID];
-    enum {WIRED, WIFI} type;
+    enum {WIRED, WIFI, VLAN} type;
     int manual; /* 1 = true, 0 = false */
 } nm_connection;

@@ -86,6 +88,11 @@ typedef struct nm_ipvX
     unsigned int                    masklen;
 }   nm_ipvX;

+typedef struct nm_vlan
+{
+    char *                          parent;
+    int                             id;
+}   nm_vlan;

 typedef struct nm_config_info
 {
@@ -95,6 +102,7 @@ typedef struct nm_config_info
     nm_wireless_security    wireless_security;
     nm_ipvX                 ipv4;
     nm_ipvX                 ipv6;
+    nm_vlan                 vlan;
 }   nm_config_info;

 /* Public functions */
diff --git a/static.c b/static.c
index ea12fba..ba03931 100644
--- a/static.c
+++ b/static.c
@@ -310,7 +310,7 @@ static int netcfg_activate_static_ipv4(struct debconfclient *client,
     deconfigure_network(NULL);

     loop_setup();
-    interface_up(interface->name);
+    netcfg_interface_up(interface);

     /* Flush all previous addresses, routes */
     snprintf(buf, sizeof(buf), "ifconfig %s inet 0 down", interface->name);
@@ -345,7 +345,7 @@ static int netcfg_activate_static_ipv4(struct debconfclient *client,
     deconfigure_network(NULL);

     loop_setup();
-    interface_up(interface->name);
+    netcfg_interface_up(interface);

     /* Flush all previous addresses, routes */
     snprintf(buf, sizeof(buf), "ip -f inet addr flush dev %s", interface->name);
@@ -426,7 +426,7 @@ static int netcfg_activate_static_ipv6(struct debconfclient *client,
     deconfigure_network(NULL);

     loop_setup();
-    interface_up(interface->name);
+    netcfg_interface_up(interface);

     /* Flush all previous addresses, routes */
     snprintf(buf, sizeof(buf), "ifconfig %s inet 0 down", interface->name);
@@ -449,7 +449,7 @@ static int netcfg_activate_static_ipv6(struct debconfclient *client,
     deconfigure_network(NULL);

     loop_setup();
-    interface_up(interface->name);
+    netcfg_interface_up(interface);

     /* Flush all previous addresses, routes */
     snprintf(buf, sizeof(buf), "ip -f inet6 addr flush dev %s", interface->name);
@@ -461,8 +461,8 @@ static int netcfg_activate_static_ipv6(struct debconfclient *client,
     /* Now down and up the interface, to get LL and SLAAC addresses back,
      * since flushing the addresses and routes gets rid of all that
      * sort of thing. */
-    interface_down(interface->name);
-    interface_up(interface->name);
+    netcfg_interface_down(interface);
+    netcfg_interface_up(interface);

     /* Add the new IP address and netmask */
     snprintf(buf, sizeof(buf), "ip addr add %s/%d dev %s",
diff --git a/vlan.c b/vlan.c
new file mode 100644
index 0000000..bd474d4
--- /dev/null
+++ b/vlan.c
@@ -0,0 +1,101 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cdebconf/debconfclient.h>
+#include <debian-installer.h>
+#include "netcfg.h"
+
+static char* get_vlan_command(const char* parentif, const char* vlanif, int vlanid) {
+#if defined(__linux__)
+	const char* vlan_command = "ip link add link %s name %s type vlan id %d";
+	int len = strlen(vlan_command) + strlen(parentif) + strlen(vlanif) + 4 + 1;
+	char* buf = malloc(len);
+	snprintf(buf, len, vlan_command, parentif, vlanif, vlanid);
+	return buf;
+#elif defined(__FreeBSD_kernel__)
+	const char* vlan_command = "ifconfig %s vlan %d vlandev %s";
+	int len = strlen(vlan_command) + strlen(parentif) + strlen(vlanif) + 4 + 1;
+	char* buf = malloc(len);
+	snprintf(buf, len, vlan_command, vlanif, vlanid, parentif);
+	return buf;
+#endif
+}
+
+/* Create a new VLAN interface attached to the currently selected
+ * network interface.
+ */
+int netcfg_set_vlan (struct debconfclient *client, struct netcfg_interface *interface) {
+#if defined(__linux__) || defined(__FreeBSD_kernel__)
+	int vlanid;
+
+	debconf_get(client, "netcfg/vlan_id");
+	/* Empty string: no VLAN preseeded, ask if we should configure VLAN */
+	if (strlen(client->value) == 0) {
+		debconf_input (client, "medium", "netcfg/use_vlan");
+		if (debconf_go(client) == GO_BACK) {
+			return GO_BACK;
+		}
+
+		debconf_get(client, "netcfg/use_vlan");
+
+		if (!strcmp(client->value, "false")) {
+			return 0;
+		}
+
+		debconf_input(client, "critical", "netcfg/vlan_id");
+		if (debconf_go(client) == GO_BACK) {
+			return GO_BACK;
+		}
+		debconf_get(client, "netcfg/vlan_id");
+	}
+
+	for (;;) {
+		vlanid = strtol(client->value, NULL, 10);
+		/* Valid range: 1-4094 (0 and 4095 are reserved.)
+		 * 0 will be returned by strtol if the value cannot be parsed.
+		 */
+		if (vlanid < 1 || vlanid > 4094) {
+			di_error("VLAN ID \"%s\" is invalid.", client->value);
+			debconf_subst(client, "netcfg/vlan_id_invalid", "vlan_id", client->value);
+			debconf_input(client, "critical", "netcfg/invalid_vlan");
+			debconf_capb(client);
+			debconf_go(client);
+
+			debconf_capb(client, "backup");
+			debconf_input(client, "critical", "netcfg/vlan_id");
+			if (debconf_go(client) == GO_BACK) {
+				return GO_BACK;
+			}
+		} else {
+			break;
+		}
+	}
+
+	int vlaniflen = strlen(interface->name) + 1 + 4 + 1;
+	char* vlanif = malloc(vlaniflen);
+	snprintf(vlanif, vlaniflen, "%s.%d", interface->name, vlanid);
+
+	char *vlan_command = get_vlan_command(interface->name, vlanif, vlanid);
+	int rc = di_exec_shell_log(vlan_command);
+	if (rc != 0) {
+		di_error("\"%s\" failed to create VLAN interface; return code = %d", vlan_command, rc);
+		free(vlan_command);
+		debconf_input(client, "critical", "netcfg/vlan_failed");
+		debconf_go(client);
+		return GO_BACK;
+	}
+	free(vlan_command);
+
+	interface->parentif = interface->name;
+	interface->name = vlanif;
+	interface->vlanid = vlanid;
+	di_exec_shell_log("apt-install vlan");
+#else
+	/* This platform does not support VLANs. */
+	debconf_get(client, "netcfg/vlan_id");
+	if (strlen(client->value) > 0) {
+		di_error("netcfg/vlan_id specified, yet VLAN is not supported on this platfrom");
+	}
+#endif
+	return 0;
+}
diff --git a/wireless.c b/wireless.c
index 566b032..83b1d1d 100644
--- a/wireless.c
+++ b/wireless.c
@@ -121,7 +121,7 @@ int netcfg_wireless_show_essids(struct debconfclient *client, struct netcfg_inte
     int essid_list_len = 1;

     iw_get_basic_config (wfd, interface->name, &wconf);
-    interface_up(interface->name);
+    netcfg_interface_up(interface);

     if (iw_scan(wfd, interface->name, iw_get_kernel_we_version(),
                 &network_list) >= 0 ) {
@@ -205,7 +205,7 @@ int netcfg_wireless_show_essids(struct debconfclient *client, struct netcfg_inte
     }

     iw_set_basic_config(wfd, interface->name, &wconf);
-    interface_down(interface->name);
+    netcfg_interface_down(interface);

     di_info("Network chosen: %s. Proceeding to connect.", interface->essid);

diff --git a/write_interface.c b/write_interface.c
index 2ab1a34..e768002 100644
--- a/write_interface.c
+++ b/write_interface.c
@@ -44,6 +44,20 @@ static int nc_wi_loopback(const struct netcfg_interface *interface, FILE *fd)
 	return 1;
 }

+/* Write VLAN settings, such as: vlan_raw_device eth0
+*/
+static int nc_wi_vlan(const struct netcfg_interface *interface, FILE *fd)
+{
+	int rv;
+	rv = 1;
+	if (interface && interface->parentif &&
+	    (fprintf(fd, "\tvlan_raw_device %s\n", interface->parentif) < 0)) {
+		rv = 0;
+	}
+	return rv;
+}
+
+
 static int nc_wi_wireless_options(const struct netcfg_interface *interface, FILE *fd)
 {
 	/*
@@ -271,7 +285,10 @@ int netcfg_write_interface(const struct netcfg_interface *interface)
 		di_debug("Writing static IPv6 stanza for %s", interface->name);
 		rv = nc_wi_static_ipv6(interface, fd);
 	}
-
+	if (rv && interface && interface->parentif) {
+		di_debug("Writing VLAN: %s", interface->name);
+		rv = nc_wi_vlan(interface, fd);
+	}
 	if (rv && interface && is_wireless_iface(interface->name)) {
 		di_debug("Writing wireless options for %s", interface->name);
 		rv = nc_wi_wireless_options(interface, fd);

#433568#174
Date:
2016-04-23 11:59:18 UTC
From:
To:
From a patch point of view I'm mostly happy. But this should incorporate the
newly suggested templates as well. Plus there are a few nits below.

Please capitalize VLAN and put Closes into parantheses.

Suggested patch description:

    Add VLAN support.

    This is based on YunQiang Su patch and Philipp Kern's rewrite, with own
    additions.

    Closes: #433568

id=%d, no?

Would be nice to have this tested once, if it actually produces a configuration
that works. ;-)

Would this also need to write out wired_specific_options for the
parentif? (The only one being the MAC address...) I'm not sure if it'd
need to be a separate file then. Probably? |:

Kind regards and thanks
Philipp Kern

#433568#179
Date:
2016-06-27 11:54:50 UTC
From:
To:
Hi,

I've installed Debian jessie in a setup yesterday, where I needed to use
vlan, so I was very happy when I saw that this bug has a patch.

However, the network card I used works nicely for configurations without
vlans without loading additional firmware, while it needs a non free
firmware to support vlans.

Looking through the patch for #433568 I could not see anything dealing
with such situations however :/

#433568#184
Date:
2016-06-27 12:05:53 UTC
From:
To:
I've never heard of that situation before.  Which hardware is this?
Where is the need for firmware documented?

Ben.

#433568#189
Date:
2016-06-27 14:13:33 UTC
From:
To:
the server is located in a loud+dusty+dark room, has 4 ethernet devices
and it seems I mixed them up…

eth2 needs the non-free firmware but eth3 is the one actually connected
to the switch…

63: None 02.0: 10701 Ethernet
  SysFS ID: /class/net/eth2
  SysFS Device Link: /devices/pci0000:00/0000:00:01.1/0000:02:00.0
  Hardware Class: network interface
  Model: "Ethernet network interface"
  Driver: "cxgb3"
  Driver Modules: "cxgb3"
  Device File: eth2
  Link detected: no

64: None 03.0: 10701 Ethernet
  SysFS ID: /class/net/eth3
  SysFS Device Link: /devices/pci0000:00/0000:00:1c.4/0000:04:00.0
  Hardware Class: network interface
  Model: "Ethernet network interface"
  Driver: "e1000e"
  Driver Modules: "e1000e"
  Device File: eth3

so, sorry for the noise.

#433568#194
Date:
2017-11-02 11:24:06 UTC
From:
To:
Hi,

Has this patch already been accepted? Any more info on how to use it, in
that case?

#433568#199
Date:
2019-01-12 11:04:18 UTC
From:
To:
This bug about adding vlan support has a patch, however nothing has been applied
yet.

Thus I'm removing the pending tag.


Holger

#433568#206
Date:
2020-04-14 07:24:40 UTC
From:
To:
Hi there,

it's long ago this issue was opened and some working code was added by
patches. :-)

On my day job we now hit exactly this situation for some months now. Our
network infrastructure got renewed and mostly all physical ports now
have VLANs tagged only.

I remembered this report and extracted the patches and information
around, put it all together on top of the Git tree for the installer and
build various variants based on the master tree for the d-i but also for
the tree of buster. We were able to configure the network setup within
the d-i to recognize the VLAN ID and proceeded successfully further with
the installation of servers.

Technically it is working, as pointed out within this report some issues
regarding debconf are still needed to work on.

I attach the current WIP of my patch work here. I reworked the source a
little bit to follow the existing coding style for the new files and
routines. But there is nothing technically changed in the new source
files.

I'd appreciate if we could get one step closer to include VLAN support
for the D-I as possible release goal for bulleseye. If possible I work
on further needed adjustments.

Thanks and Regards
Carsten

#433568#211
Date:
2021-12-28 01:27:28 UTC
From:
To:
[image: image.png]
#433568#216
Date:
2022-01-31 09:44:47 UTC
From:
To:
[image: image.png]
#433568#221
Date:
2024-02-22 08:16:24 UTC
From:
To:
Hello dear Debian maintainers,

I can confirm that the 2020 patch from Carsten Schoenert still applies
to d-i/netcfg releases 1.187 and 1.188, and result in a debian installer
that knows about VLANs and can be fully automated with a preseed config
like the following:

d-i netcfg/enable                boolean true
d-i netcfg/use_vlan              boolean true
d-i netcfg/vlan_id               string  1001
d-i netcfg/choose_interface      select  ens3f0np0


We would be interested in helping push the patch upstream so that other
users can enjoy this without having to build and make their own initrd,
how can we help?


Regards,

#433568#226
Date:
2024-05-27 18:06:26 UTC
From:
To:
Sorry, I have to disagree with you here -- the patch certainly does not apply
cleanly anymore. Even after fixing it up, I faced installation issues as it
attempts to add the VLAN subinterface twice, and fails the second time due to
it already existing.

The VLAN code does not use ioctls/netlink and instead shells out to ip link/
ifconfig for Linux and FreeBSD respectively. I took a quick look at what it
would take to shift this code over to ioctls, but it seems there are inherent
differences in the ioctl interface between the two OS-es that would make it
non-trivial to support both at that level. I would be curious to hear the
maintainers feedback as to whether or not this is acceptable.

I'm attaching a patch that applies cleanly against 1.188 and was tested
against a patched bookworm initrd with success.

Cheers,
Tyler

#433568#231
Date:
2024-05-27 18:20:00 UTC
From:
To:
On Mon, May 27, 2024 at 2:02 PM Tyler Stachecki <stachecki.tyler@gmail.com> wrote:

Sorry -- turns out this was a little rash on my part: the patch from
2020 applies to 1.188 with fuzz but not 1.889 (which is what I had
checked out). Anywho - the patch I attached previously applies cleanly
to 1.189 and I am similarly happy to act on any feedback from the
maintainer to carry this one across the line.

Cheers,
Tyler

#433568#236
Date:
2024-06-22 18:02:02 UTC
From:
To:
It's not impossible already to do VLANs with d-i currently (for anyone
reading this after a temporary work around until this is properly fixed)...

Step 1) Get to the screen where d-i presents you with a list of network
interfaces

Step 2) Go to VT2 (using alt-F2) and activate the console

Step 3) Add a VLAN interface to whichever physical interface you want it
on... ip link add link eno1 name eno1.42 type vlan id 42

Step 4) Go back to VT1 (using alt+F1) and choose to go 'Back' and then
forward by selecting 'Configure the network' to repopulate the list

Step 5) Pick the new VLAN interface until you're at the screen where
you'd normally pick 'Configure network manually...' (but don't pick this
option yet)

Step 6) Go BACK to VT2 (using alt-F2)

Step 7) Bring the physical and VLAN interfaces up... ip link set eno1
up; ip link set eno1.42 up

Step 8) Go BACK to VT1 (using alt+F1) and continue as normal

EASY!.. OK... FINE... I admit that is sarcasm.  It's also laughable that
it's easier to get d-i to use PPPoE than it is a VLAN.

Now you might think to do the commands on VT2 all at once, but I find
once you've picked the network interface with d-i it'll bring the
interface down!

Also these instructions are useless if you're not configuring a static
IP as you'll have to be super quick before the autoconfiguration fails.

Lastly... (this is the main reason I'm adding to this bug report)...

Does the new patch discussed here address the fact that usually I find I
have to run this (AFTER the system has booted up for the first time)...

sed -i 's/allow-hotplug/auto/' /etc/network/interfaces

... and then reboot again before any networking works?

Because 'allow-hotplug' just doesn't cut it with VLAN interfaces... it
seems it must be 'auto'

Frankly I don't see why we don't use 'auto' in the default
/etc/network/interfaces (whether using a VLAN or not) as it covers
everything 'allow-hotplug' does but actually works in more cases.

Thoughts?

#433568#241
Date:
2025-04-09 16:52:17 UTC
From:
To:
Dear Debian Maintainers,

just got bitten by this in bookworm as well and had to built my own version of netcfg since the manual workaround mentioned by Steven is not really feasible (thanks anyway for this) and I can only add my two thoughts as:

  - Carsten's patch works like a charm
  - It would be great if you could include it in the next release

Best Regards,
Urs