Browse Source

[vlan] Add support for IEEE 802.1Q VLANs

Originally-implemented-by: michael-dev@fami-braun.de
Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 13 years ago
parent
commit
6fd09b541f

+ 3
- 0
src/config/config.c View File

@@ -234,6 +234,9 @@ REQUIRE_OBJECT ( pxe_cmd );
234 234
 #ifdef LOTEST_CMD
235 235
 REQUIRE_OBJECT ( lotest_cmd );
236 236
 #endif
237
+#ifdef VLAN_CMD
238
+REQUIRE_OBJECT ( vlan_cmd );
239
+#endif
237 240
 
238 241
 /*
239 242
  * Drag in miscellaneous objects

+ 1
- 0
src/config/general.h View File

@@ -123,6 +123,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
123 123
 #undef	TIME_CMD		/* Time commands */
124 124
 #undef	DIGEST_CMD		/* Image crypto digest commands */
125 125
 #undef	LOTEST_CMD		/* Loopback testing commands */
126
+#undef	VLAN_CMD		/* VLAN commands */
126 127
 //#undef	PXE_CMD			/* PXE commands */
127 128
 
128 129
 /*

+ 174
- 0
src/hci/commands/vlan_cmd.c View File

@@ -0,0 +1,174 @@
1
+/*
2
+ * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+FILE_LICENCE ( GPL2_OR_LATER );
20
+
21
+#include <stdio.h>
22
+#include <stdlib.h>
23
+#include <string.h>
24
+#include <getopt.h>
25
+#include <ipxe/netdevice.h>
26
+#include <ipxe/command.h>
27
+#include <ipxe/vlan.h>
28
+
29
+/** @file
30
+ *
31
+ * VLAN commands
32
+ *
33
+ */
34
+
35
+static void vcreate_syntax ( char **argv ) {
36
+	printf ( "Usage:\n  %s --tag <tag> [--priority <priority] "
37
+		 "<trunk interface>\n", argv[0] );
38
+}
39
+
40
+static int vcreate_exec ( int argc, char **argv ) {
41
+	static struct option vcreate_opts[] = {
42
+		{ "help", 0, NULL, 'h' },
43
+		{ "tag", required_argument, NULL, 't' },
44
+		{ "priority", required_argument, NULL, 'p' },
45
+		{ NULL, 0, NULL, 0 },
46
+	};
47
+	const char *trunk_name;
48
+	const char *tag_text = NULL;
49
+	const char *priority_text = NULL;
50
+	struct net_device *trunk;
51
+	unsigned int tag;
52
+	unsigned int priority;
53
+	char *endp;
54
+	int c;
55
+	int rc;
56
+
57
+	/* Parse command line */
58
+	while ( ( c = getopt_long ( argc, argv, "ht:p:", vcreate_opts,
59
+				    NULL ) ) >= 0 ) {
60
+		switch ( c ) {
61
+		case 't':
62
+			tag_text = optarg;
63
+			break;
64
+		case 'p':
65
+			priority_text = optarg;
66
+			break;
67
+		case 'h':
68
+			/* Display help text */
69
+		default:
70
+			/* Unrecognised/invalid option */
71
+			vcreate_syntax ( argv );
72
+			return 1;
73
+		}
74
+	}
75
+	if ( optind != ( argc - 1 ) ) {
76
+		vcreate_syntax ( argv );
77
+		return 1;
78
+	}
79
+	trunk_name = argv[optind];
80
+	if ( ! tag_text ) {
81
+		vcreate_syntax ( argv );
82
+		return 1;
83
+	}
84
+
85
+	/* Identify network device */
86
+	trunk = find_netdev ( trunk_name );
87
+	if ( ! trunk ) {
88
+		printf ( "%s: no such interface\n", trunk_name );
89
+		return 1;
90
+	}
91
+	tag = strtoul ( tag_text, &endp, 10 );
92
+	if ( *endp ) {
93
+		printf ( "%s: invalid tag\n", tag_text );
94
+		return 1;
95
+	}
96
+	if ( priority_text ) {
97
+		priority = strtoul ( priority_text, &endp, 10 );
98
+		if ( *endp ) {
99
+			printf ( "%s: invalid priority\n", priority_text );
100
+			return 1;
101
+		}
102
+	} else {
103
+		priority = 0;
104
+	}
105
+
106
+	/* Create VLAN device */
107
+	if ( ( rc = vlan_create ( trunk, tag, priority ) ) != 0 ) {
108
+		printf ( "Could not create VLAN device: %s\n",
109
+			 strerror ( rc ) );
110
+		return 1;
111
+	}
112
+
113
+	return 0;
114
+}
115
+
116
+static void vdestroy_syntax ( char **argv ) {
117
+	printf ( "Usage:\n  %s <interface>\n", argv[0] );
118
+}
119
+
120
+static int vdestroy_exec ( int argc, char **argv ) {
121
+	static struct option vdestroy_opts[] = {
122
+		{ "help", 0, NULL, 'h' },
123
+		{ NULL, 0, NULL, 0 },
124
+	};
125
+	const char *netdev_name;
126
+	struct net_device *netdev;
127
+	int c;
128
+	int rc;
129
+
130
+	/* Parse command line */
131
+	while ( ( c = getopt_long ( argc, argv, "h", vdestroy_opts,
132
+				    NULL ) ) >= 0 ) {
133
+		switch ( c ) {
134
+		case 'h':
135
+			/* Display help text */
136
+		default:
137
+			/* Unrecognised/invalid option */
138
+			vdestroy_syntax ( argv );
139
+			return 1;
140
+		}
141
+	}
142
+	if ( optind != ( argc - 1 ) ) {
143
+		vdestroy_syntax ( argv );
144
+		return 1;
145
+	}
146
+	netdev_name = argv[optind];
147
+
148
+	/* Identify network device */
149
+	netdev = find_netdev ( netdev_name );
150
+	if ( ! netdev ) {
151
+		printf ( "%s: no such interface\n", netdev_name );
152
+		return 1;
153
+	}
154
+
155
+	/* Destroy VLAN device */
156
+	if ( ( rc = vlan_destroy ( netdev ) ) != 0 ) {
157
+		printf ( "Could not destroy VLAN device: %s\n",
158
+			 strerror ( rc ) );
159
+		return 1;
160
+	}
161
+
162
+	return 0;
163
+}
164
+
165
+struct command vlan_commands[] __command = {
166
+	{
167
+		.name = "vcreate",
168
+		.exec = vcreate_exec,
169
+	},
170
+	{
171
+		.name = "vdestroy",
172
+		.exec = vdestroy_exec,
173
+	},
174
+};

+ 1
- 0
src/include/ipxe/errfile.h View File

@@ -190,6 +190,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
190 190
 #define ERRFILE_fcp			( ERRFILE_NET | 0x002d0000 )
191 191
 #define ERRFILE_fcoe			( ERRFILE_NET | 0x002e0000 )
192 192
 #define ERRFILE_fcns			( ERRFILE_NET | 0x002f0000 )
193
+#define ERRFILE_vlan			( ERRFILE_NET | 0x00300000 )
193 194
 
194 195
 #define ERRFILE_image		      ( ERRFILE_IMAGE | 0x00000000 )
195 196
 #define ERRFILE_elf		      ( ERRFILE_IMAGE | 0x00010000 )

+ 1
- 0
src/include/ipxe/features.h View File

@@ -51,6 +51,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
51 51
 #define DHCP_EB_FEATURE_COMBOOT		0x23 /**< COMBOOT format */
52 52
 #define DHCP_EB_FEATURE_EFI		0x24 /**< EFI format */
53 53
 #define DHCP_EB_FEATURE_FCOE		0x25 /**< FCoE protocol */
54
+#define DHCP_EB_FEATURE_VLAN		0x26 /**< VLAN support */
54 55
 
55 56
 /** @} */
56 57
 

+ 1
- 0
src/include/ipxe/if_ether.h View File

@@ -18,6 +18,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
18 18
 #define ETH_P_IP	0x0800	/* Internet Protocl Packet */
19 19
 #define ETH_P_ARP	0x0806	/* Address Resolution Protocol */
20 20
 #define ETH_P_RARP	0x8035	/* Reverse Address resolution Protocol */
21
+#define ETH_P_8021Q	0x8100	/* 802.1Q VLAN Extended Header */
21 22
 #define ETH_P_IPV6	0x86DD	/* IPv6 over blueblook */
22 23
 #define ETH_P_SLOW	0x8809	/* Ethernet slow protocols */
23 24
 #define ETH_P_EAPOL	0x888E	/* 802.1X EAP over LANs */

+ 6
- 5
src/include/ipxe/netdevice.h View File

@@ -36,11 +36,12 @@ struct device;
36 36
 /** Maximum length of a link-layer header
37 37
  *
38 38
  * The longest currently-supported link-layer header is for 802.11: a
39
- * 24-byte frame header plus an 8-byte 802.3 LLC/SNAP header.  (The
40
- * IPoIB link-layer pseudo-header doesn't actually include link-layer
41
- * addresses; see ipoib.c for details).
39
+ * 24-byte frame header plus an 8-byte 802.3 LLC/SNAP header, plus a
40
+ * possible 4-byte VLAN header.  (The IPoIB link-layer pseudo-header
41
+ * doesn't actually include link-layer addresses; see ipoib.c for
42
+ * details.)
42 43
  */
43
-#define MAX_LL_HEADER_LEN 32
44
+#define MAX_LL_HEADER_LEN 36
44 45
 
45 46
 /** Maximum length of a network-layer address */
46 47
 #define MAX_NET_ADDR_LEN 4
@@ -278,7 +279,7 @@ struct net_device {
278 279
 	/** List of open network devices */
279 280
 	struct list_head open_list;
280 281
 	/** Name of this network device */
281
-	char name[8];
282
+	char name[12];
282 283
 	/** Underlying hardware device */
283 284
 	struct device *dev;
284 285
 

+ 66
- 0
src/include/ipxe/vlan.h View File

@@ -0,0 +1,66 @@
1
+#ifndef _IPXE_VLAN_H
2
+#define _IPXE_VLAN_H
3
+
4
+/**
5
+ * @file
6
+ *
7
+ * Virtual LANs
8
+ *
9
+ */
10
+
11
+FILE_LICENCE ( GPL2_OR_LATER );
12
+
13
+/** A VLAN header */
14
+struct vlan_header {
15
+	/** Tag control information */
16
+	uint16_t tci;
17
+	/** Encapsulated protocol */
18
+	uint16_t net_proto;
19
+} __attribute__ (( packed ));
20
+
21
+/**
22
+ * Extract VLAN tag from tag control information
23
+ *
24
+ * @v tci		Tag control information
25
+ * @ret tag		VLAN tag
26
+ */
27
+#define VLAN_TAG( tci ) ( (tci) & 0x0fff )
28
+
29
+/**
30
+ * Extract VLAN priority from tag control information
31
+ *
32
+ * @v tci		Tag control information
33
+ * @ret priority	Priority
34
+ */
35
+#define VLAN_PRIORITY( tci ) ( (tci) >> 13 )
36
+
37
+/**
38
+ * Construct VLAN tag control information
39
+ *
40
+ * @v tag		VLAN tag
41
+ * @v priority		Priority
42
+ * @ret tci		Tag control information
43
+ */
44
+#define VLAN_TCI( tag, priority ) ( ( (priority) << 13 ) | (tag) )
45
+
46
+/**
47
+ * Check VLAN tag is valid
48
+ *
49
+ * @v tag		VLAN tag
50
+ * @ret is_valid	VLAN tag is valid
51
+ */
52
+#define VLAN_TAG_IS_VALID( tag ) ( ( (tag) >= 1 ) && ( (tag) < 0xfff ) )
53
+
54
+/**
55
+ * Check VLAN priority is valid
56
+ *
57
+ * @v priority		VLAN priority
58
+ * @ret is_valid	VLAN priority is valid
59
+ */
60
+#define VLAN_PRIORITY_IS_VALID( priority ) ( (priority) <= 7 )
61
+
62
+extern int vlan_create ( struct net_device *trunk, unsigned int tag,
63
+			 unsigned int priority );
64
+extern int vlan_destroy ( struct net_device *netdev );
65
+
66
+#endif /* _IPXE_VLAN_H */

+ 7
- 5
src/net/netdevice.c View File

@@ -405,8 +405,10 @@ int register_netdev ( struct net_device *netdev ) {
405 405
 	int rc;
406 406
 
407 407
 	/* Create device name */
408
-	snprintf ( netdev->name, sizeof ( netdev->name ), "net%d",
409
-		   ifindex++ );
408
+	if ( netdev->name[0] == '\0' ) {
409
+		snprintf ( netdev->name, sizeof ( netdev->name ), "net%d",
410
+			   ifindex++ );
411
+	}
410 412
 
411 413
 	/* Set initial link-layer address */
412 414
 	netdev->ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr );
@@ -461,13 +463,13 @@ int netdev_open ( struct net_device *netdev ) {
461 463
 
462 464
 	DBGC ( netdev, "NETDEV %s opening\n", netdev->name );
463 465
 
466
+	/* Mark as opened */
467
+	netdev->state |= NETDEV_OPEN;
468
+
464 469
 	/* Open the device */
465 470
 	if ( ( rc = netdev->op->open ( netdev ) ) != 0 )
466 471
 		return rc;
467 472
 
468
-	/* Mark as opened */
469
-	netdev->state |= NETDEV_OPEN;
470
-
471 473
 	/* Add to head of open devices list */
472 474
 	list_add ( &netdev->open_list, &open_net_devices );
473 475
 

+ 465
- 0
src/net/vlan.c View File

@@ -0,0 +1,465 @@
1
+/*
2
+ * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+FILE_LICENCE ( GPL2_OR_LATER );
20
+
21
+#include <stdint.h>
22
+#include <string.h>
23
+#include <stdio.h>
24
+#include <errno.h>
25
+#include <byteswap.h>
26
+#include <ipxe/features.h>
27
+#include <ipxe/if_ether.h>
28
+#include <ipxe/ethernet.h>
29
+#include <ipxe/netdevice.h>
30
+#include <ipxe/iobuf.h>
31
+#include <ipxe/vlan.h>
32
+
33
+/** @file
34
+ *
35
+ * Virtual LANs
36
+ *
37
+ */
38
+
39
+FEATURE ( FEATURE_PROTOCOL, "VLAN", DHCP_EB_FEATURE_VLAN, 1 );
40
+
41
+struct net_protocol vlan_protocol __net_protocol;
42
+
43
+/** VLAN device private data */
44
+struct vlan_device {
45
+	/** Trunk network device */
46
+	struct net_device *trunk;
47
+	/** VLAN tag */
48
+	unsigned int tag;
49
+	/** Default priority */
50
+	unsigned int priority;
51
+};
52
+
53
+/**
54
+ * Open VLAN device
55
+ *
56
+ * @v netdev		Network device
57
+ * @ret rc		Return status code
58
+ */
59
+static int vlan_open ( struct net_device *netdev ) {
60
+	struct vlan_device *vlan = netdev->priv;
61
+
62
+	return netdev_open ( vlan->trunk );
63
+}
64
+
65
+/**
66
+ * Close VLAN device
67
+ *
68
+ * @v netdev		Network device
69
+ */
70
+static void vlan_close ( struct net_device *netdev ) {
71
+	struct vlan_device *vlan = netdev->priv;
72
+
73
+	netdev_close ( vlan->trunk );
74
+}
75
+
76
+/**
77
+ * Transmit packet on VLAN device
78
+ *
79
+ * @v netdev		Network device
80
+ * @v iobuf		I/O buffer
81
+ * @ret rc		Return status code
82
+ */
83
+static int vlan_transmit ( struct net_device *netdev,
84
+			   struct io_buffer *iobuf ) {
85
+	struct vlan_device *vlan = netdev->priv;
86
+	struct net_device *trunk = vlan->trunk;
87
+	struct ll_protocol *ll_protocol;
88
+	struct vlan_header *vlanhdr;
89
+	uint8_t ll_dest_copy[ETH_ALEN];
90
+	uint8_t ll_source_copy[ETH_ALEN];
91
+	const void *ll_dest;
92
+	const void *ll_source;
93
+	uint16_t net_proto;
94
+	int rc;
95
+
96
+	/* Strip link-layer header and preserve link-layer header fields */
97
+	ll_protocol = netdev->ll_protocol;
98
+	if ( ( rc = ll_protocol->pull ( netdev, iobuf, &ll_dest, &ll_source,
99
+					&net_proto ) ) != 0 ) {
100
+		DBGC ( netdev, "VLAN %s could not parse link-layer header: "
101
+		       "%s\n", netdev->name, strerror ( rc ) );
102
+		return rc;
103
+	}
104
+	memcpy ( ll_dest_copy, ll_dest, ETH_ALEN );
105
+	memcpy ( ll_source_copy, ll_source, ETH_ALEN );
106
+
107
+	/* Construct VLAN header */
108
+	vlanhdr = iob_push ( iobuf, sizeof ( *vlanhdr ) );
109
+	vlanhdr->tci = htons ( VLAN_TCI ( vlan->tag, vlan->priority ) );
110
+	vlanhdr->net_proto = net_proto;
111
+
112
+	/* Reclaim I/O buffer from VLAN device's TX queue */
113
+	list_del ( &iobuf->list );
114
+
115
+	/* Transmit packet on trunk device */
116
+	if ( ( rc = net_tx ( iob_disown ( iobuf ), trunk, &vlan_protocol,
117
+			     ll_dest_copy, ll_source_copy ) ) != 0 ) {
118
+		DBGC ( netdev, "VLAN %s could not transmit: %s\n",
119
+		       netdev->name, strerror ( rc ) );
120
+		/* Cannot return an error status, since that would
121
+		 * cause the I/O buffer to be double-freed.
122
+		 */
123
+		return 0;
124
+	}
125
+
126
+	return 0;
127
+}
128
+
129
+/**
130
+ * Poll VLAN device
131
+ *
132
+ * @v netdev		Network device
133
+ */
134
+static void vlan_poll ( struct net_device *netdev ) {
135
+	struct vlan_device *vlan = netdev->priv;
136
+
137
+	/* Poll trunk device */
138
+	netdev_poll ( vlan->trunk );
139
+}
140
+
141
+/**
142
+ * Enable/disable interrupts on VLAN device
143
+ *
144
+ * @v netdev		Network device
145
+ * @v enable		Interrupts should be enabled
146
+ */
147
+static void vlan_irq ( struct net_device *netdev, int enable ) {
148
+	struct vlan_device *vlan = netdev->priv;
149
+
150
+	/* Enable/disable interrupts on trunk device.  This is not at
151
+	 * all robust, but there is no sensible course of action
152
+	 * available.
153
+	 */
154
+	netdev_irq ( vlan->trunk, enable );
155
+}
156
+
157
+/** VLAN device operations */
158
+static struct net_device_operations vlan_operations = {
159
+	.open		= vlan_open,
160
+	.close		= vlan_close,
161
+	.transmit	= vlan_transmit,
162
+	.poll		= vlan_poll,
163
+	.irq		= vlan_irq,
164
+};
165
+
166
+/**
167
+ * Synchronise VLAN device
168
+ *
169
+ * @v netdev		Network device
170
+ */
171
+static void vlan_sync ( struct net_device *netdev ) {
172
+	struct vlan_device *vlan = netdev->priv;
173
+	struct net_device *trunk = vlan->trunk;
174
+
175
+	/* Synchronise link status */
176
+	if ( netdev->link_rc != trunk->link_rc )
177
+		netdev_link_err ( netdev, trunk->link_rc );
178
+
179
+	/* Synchronise open/closed status */
180
+	if ( netdev_is_open ( trunk ) ) {
181
+		if ( ! netdev_is_open ( netdev ) )
182
+			netdev_open ( netdev );
183
+	} else {
184
+		if ( netdev_is_open ( netdev ) )
185
+			netdev_close ( netdev );
186
+	}
187
+}
188
+
189
+/**
190
+ * Identify VLAN device
191
+ *
192
+ * @v trunk		Trunk network device
193
+ * @v tag		VLAN tag
194
+ * @ret netdev		VLAN device, if any
195
+ */
196
+static struct net_device * vlan_find ( struct net_device *trunk,
197
+				       uint16_t tag ) {
198
+	struct net_device *netdev;
199
+	struct vlan_device *vlan;
200
+
201
+	for_each_netdev ( netdev ) {
202
+		if ( netdev->op != &vlan_operations )
203
+			continue;
204
+		vlan = netdev->priv;
205
+		if ( ( vlan->trunk == trunk ) && ( vlan->tag == tag ) )
206
+			return netdev;
207
+	}
208
+	return NULL;
209
+}
210
+
211
+/**
212
+ * Process incoming VLAN packet
213
+ *
214
+ * @v iobuf		I/O buffer
215
+ * @v trunk		Trunk network device
216
+ * @v ll_dest		Link-layer destination address
217
+ * @v ll_source		Link-layer source address
218
+ * @ret rc		Return status code
219
+ */
220
+static int vlan_rx ( struct io_buffer *iobuf, struct net_device *trunk,
221
+		     const void *ll_dest, const void *ll_source ) {
222
+	struct vlan_header *vlanhdr = iobuf->data;
223
+	struct net_device *netdev;
224
+	struct ll_protocol *ll_protocol;
225
+	uint8_t ll_dest_copy[ETH_ALEN];
226
+	uint8_t ll_source_copy[ETH_ALEN];
227
+	uint16_t tag;
228
+	int rc;
229
+
230
+	/* Sanity check */
231
+	if ( iob_len ( iobuf ) < sizeof ( *vlanhdr ) ) {
232
+		DBGC ( trunk, "VLAN %s received underlength packet (%zd "
233
+		       "bytes)\n", trunk->name, iob_len ( iobuf ) );
234
+		rc = -EINVAL;
235
+		goto err_sanity;
236
+	}
237
+
238
+	/* Identify VLAN device */
239
+	tag = VLAN_TAG ( ntohs ( vlanhdr->tci ) );
240
+	netdev = vlan_find ( trunk, tag );
241
+	if ( ! netdev ) {
242
+		DBGC2 ( trunk, "VLAN %s received packet for unknown VLAN "
243
+			"%d\n", trunk->name, tag );
244
+		rc = -EPIPE;
245
+		goto err_no_vlan;
246
+	}
247
+
248
+	/* Strip VLAN header and preserve original link-layer header fields */
249
+	iob_pull ( iobuf, sizeof ( *vlanhdr ) );
250
+	ll_protocol = trunk->ll_protocol;
251
+	memcpy ( ll_dest_copy, ll_dest, ETH_ALEN );
252
+	memcpy ( ll_source_copy, ll_source, ETH_ALEN );
253
+
254
+	/* Reconstruct link-layer header for VLAN device */
255
+	ll_protocol = netdev->ll_protocol;
256
+	if ( ( rc = ll_protocol->push ( netdev, iobuf, ll_dest_copy,
257
+					ll_source_copy,
258
+					vlanhdr->net_proto ) ) != 0 ) {
259
+		DBGC ( netdev, "VLAN %s could not reconstruct link-layer "
260
+		       "header: %s\n", netdev->name, strerror ( rc ) );
261
+		goto err_ll_push;
262
+	}
263
+
264
+	/* Enqueue packet on VLAN device */
265
+	netdev_rx ( netdev, iob_disown ( iobuf ) );
266
+	return 0;
267
+
268
+ err_ll_push:
269
+ err_no_vlan:
270
+ err_sanity:
271
+	free_iob ( iobuf );
272
+	return rc;
273
+}
274
+
275
+/** VLAN protocol */
276
+struct net_protocol vlan_protocol __net_protocol = {
277
+	.name = "VLAN",
278
+	.net_proto = htons ( ETH_P_8021Q ),
279
+	.rx = vlan_rx,
280
+};
281
+
282
+/**
283
+ * Create VLAN device
284
+ *
285
+ * @v trunk		Trunk network device
286
+ * @v tag		VLAN tag
287
+ * @v priority		Default VLAN priority
288
+ * @ret rc		Return status code
289
+ *
290
+ * The VLAN device will be created as an Ethernet device.  (We cannot
291
+ * simply clone the link layer of the trunk network device, because
292
+ * this link layer may expect the network device structure to contain
293
+ * some link-layer-private data.)  The trunk network device must
294
+ * therefore have a link layer that is in some sense 'compatible' with
295
+ * Ethernet; specifically, it must have link-layer addresses that are
296
+ * the same length as Ethernet link-layer addresses.
297
+ */
298
+int vlan_create ( struct net_device *trunk, unsigned int tag,
299
+		  unsigned int priority ) {
300
+	struct net_device *netdev;
301
+	struct vlan_device *vlan;
302
+	int rc;
303
+
304
+	/* Sanity checks */
305
+	if ( trunk->ll_protocol->ll_addr_len != ETH_ALEN ) {
306
+		DBGC ( trunk, "VLAN %s cannot create VLAN for %s device\n",
307
+		       trunk->name, trunk->ll_protocol->name );
308
+		rc = -ENOTTY;
309
+		goto err_sanity;
310
+	}
311
+	if ( ! VLAN_TAG_IS_VALID ( tag ) ) {
312
+		DBGC ( trunk, "VLAN %s cannot create VLAN with invalid tag "
313
+		       "%d\n", trunk->name, tag );
314
+		rc = -EINVAL;
315
+		goto err_sanity;
316
+	}
317
+	if ( ! VLAN_PRIORITY_IS_VALID ( priority ) ) {
318
+		DBGC ( trunk, "VLAN %s cannot create VLAN with invalid "
319
+		       "priority %d\n", trunk->name, priority );
320
+		rc = -EINVAL;
321
+		goto err_sanity;
322
+	}
323
+	if ( ( netdev = vlan_find ( trunk, tag ) ) != NULL ) {
324
+		DBGC ( netdev, "VLAN %s already exists\n", netdev->name );
325
+		rc = -EEXIST;
326
+		goto err_sanity;
327
+	}
328
+
329
+	/* Allocate and initialise structure */
330
+	netdev = alloc_etherdev ( sizeof ( *vlan ) );
331
+	if ( ! netdev ) {
332
+		rc = -ENOMEM;
333
+		goto err_alloc_etherdev;
334
+	}
335
+	netdev_init ( netdev, &vlan_operations );
336
+	netdev->dev = trunk->dev;
337
+	memcpy ( netdev->hw_addr, trunk->ll_addr, ETH_ALEN );
338
+	vlan = netdev->priv;
339
+	vlan->trunk = netdev_get ( trunk );
340
+	vlan->tag = tag;
341
+	vlan->priority = priority;
342
+
343
+	/* Construct VLAN device name */
344
+	snprintf ( netdev->name, sizeof ( netdev->name ), "%s.%d",
345
+		   trunk->name, vlan->tag );
346
+
347
+	/* Register VLAN device */
348
+	if ( ( rc = register_netdev ( netdev ) ) != 0 ) {
349
+		DBGC ( netdev, "VLAN %s could not register: %s\n",
350
+		       netdev->name, strerror ( rc ) );
351
+		goto err_register;
352
+	}
353
+
354
+	/* Synchronise with trunk device */
355
+	vlan_sync ( netdev );
356
+
357
+	return 0;
358
+
359
+	unregister_netdev ( netdev );
360
+ err_register:
361
+	netdev_nullify ( netdev );
362
+	netdev_put ( netdev );
363
+	netdev_put ( trunk );
364
+ err_alloc_etherdev:
365
+ err_sanity:
366
+	return rc;
367
+}
368
+
369
+/**
370
+ * Destroy VLAN device
371
+ *
372
+ * @v netdev		Network device
373
+ * @ret rc		Return status code
374
+ */
375
+int vlan_destroy ( struct net_device *netdev ) {
376
+	struct vlan_device *vlan = netdev->priv;
377
+	struct net_device *trunk;
378
+
379
+	/* Sanity check */
380
+	if ( netdev->op != &vlan_operations ) {
381
+		DBGC ( netdev, "VLAN %s cannot destroy non-VLAN device\n",
382
+		       netdev->name );
383
+		return -ENOTTY;
384
+	}
385
+
386
+	/* Remove VLAN device */
387
+	unregister_netdev ( netdev );
388
+	trunk = vlan->trunk;
389
+	netdev_nullify ( netdev );
390
+	netdev_put ( netdev );
391
+	netdev_put ( trunk );
392
+
393
+	return 0;
394
+}
395
+
396
+/**
397
+ * Do nothing
398
+ *
399
+ * @v trunk		Trunk network device
400
+ * @ret rc		Return status code
401
+ */
402
+static int vlan_probe ( struct net_device *trunk __unused ) {
403
+	return 0;
404
+}
405
+
406
+/**
407
+ * Handle trunk network device link state change
408
+ *
409
+ * @v trunk		Trunk network device
410
+ */
411
+static void vlan_notify ( struct net_device *trunk ) {
412
+	struct net_device *netdev;
413
+	struct vlan_device *vlan;
414
+
415
+	for_each_netdev ( netdev ) {
416
+		if ( netdev->op != &vlan_operations )
417
+			continue;
418
+		vlan = netdev->priv;
419
+		if ( vlan->trunk == trunk )
420
+			vlan_sync ( netdev );
421
+	}
422
+}
423
+
424
+/**
425
+ * Destroy first VLAN device for a given trunk
426
+ *
427
+ * @v trunk		Trunk network device
428
+ * @ret found		A VLAN device was found
429
+ */
430
+static int vlan_remove_first ( struct net_device *trunk ) {
431
+	struct net_device *netdev;
432
+	struct vlan_device *vlan;
433
+
434
+	for_each_netdev ( netdev ) {
435
+		if ( netdev->op != &vlan_operations )
436
+			continue;
437
+		vlan = netdev->priv;
438
+		if ( vlan->trunk == trunk ) {
439
+			vlan_destroy ( netdev );
440
+			return 1;
441
+		}
442
+	}
443
+	return 0;
444
+}
445
+
446
+/**
447
+ * Destroy all VLAN devices for a given trunk
448
+ *
449
+ * @v trunk		Trunk network device
450
+ */
451
+static void vlan_remove ( struct net_device *trunk ) {
452
+
453
+	/* Remove all VLAN devices attached to this trunk, safe
454
+	 * against arbitrary net device removal.
455
+	 */
456
+	while ( vlan_remove_first ( trunk ) ) {}
457
+}
458
+
459
+/** VLAN driver */
460
+struct net_driver vlan_driver __net_driver = {
461
+	.name = "VLAN",
462
+	.probe = vlan_probe,
463
+	.notify = vlan_notify,
464
+	.remove = vlan_remove,
465
+};

Loading…
Cancel
Save