Loading...
   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
/*
 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright 1998 Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby
 * granted, provided that both the above copyright notice and this
 * permission notice appear in all copies, that both the above
 * copyright notice and this permission notice appear in all
 * supporting documentation, and that the name of M.I.T. not be used
 * in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.  M.I.T. makes
 * no representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied
 * warranty.
 * 
 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: src/sys/net/if_vlan.c,v 1.54 2003/10/31 18:32:08 brooks Exp $
 */

/*
 * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
 * Might be extended some day to also handle IEEE 802.1p priority
 * tagging.  This is sort of sneaky in the implementation, since
 * we need to pretend to be enough of an Ethernet implementation
 * to make arp work.  The way we do this is by telling everyone
 * that we are an Ethernet, and then catch the packets that
 * ether_output() left on our output queue when it calls
 * if_start(), rewrite them for use by the real outgoing interface,
 * and ask it to send them.
 */


#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/kern_event.h>

#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/if_vlan_var.h>

#include <net/dlil.h>

#ifdef INET
#include <netinet/in.h>
#include <netinet/if_ether.h>
#endif

#include <net/if_media.h>

#define	ETHER_VLAN_ENCAP_LEN	4	/* len of 802.1Q VLAN encapsulation */
#define	IF_MAXUNIT	0x7fff		/* historical value */

#define IFP2AC(p) ((struct arpcom *)p)

#define VLAN_PROTO_FAMILY	0x766c616e /* 'vlan' */

#define VLANNAME	"vlan"

typedef int (bpf_callback_func)(struct ifnet *, struct mbuf *);
typedef int (if_set_bpf_tap_func)(struct ifnet *ifp, int mode, bpf_callback_func * func);

struct vlan_mc_entry {
    struct ether_addr		mc_addr;
    SLIST_ENTRY(vlan_mc_entry)	mc_entries;
};

struct	ifvlan {
    char	ifv_name[IFNAMSIZ]; /* our unique id */
    struct  ifnet *ifv_ifp;  /* our interface */
    struct	ifnet *ifv_p;	/* parent interface of this vlan */
    struct	ifv_linkmib {
	int	ifvm_parent;
	int	ifvm_encaplen;	/* encapsulation length */
	int	ifvm_mtufudge;	/* MTU fudged by this much */
	int	ifvm_mintu;	/* min transmission unit */
	u_int16_t ifvm_proto; /* encapsulation ethertype */
	u_int16_t ifvm_tag; /* tag to apply on packets leaving if */
    }	ifv_mib;
    SLIST_HEAD(__vlan_mchead, vlan_mc_entry)	vlan_mc_listhead;
    LIST_ENTRY(ifvlan) ifv_list;
    int	ifv_flags;
    int ifv_detaching;
    u_long ifv_filter_id;
    int ifv_filter_valid;
    bpf_callback_func *	ifv_bpf_input;
    bpf_callback_func * ifv_bpf_output;
};

#define	ifv_tag	ifv_mib.ifvm_tag
#define	ifv_encaplen	ifv_mib.ifvm_encaplen
#define	ifv_mtufudge	ifv_mib.ifvm_mtufudge
#define	ifv_mintu	ifv_mib.ifvm_mintu

#define	IFVF_PROMISC		0x01		/* promiscuous mode enabled */

#if 0
SYSCTL_DECL(_net_link);
SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency");
#endif 0

#define M_VLAN 		M_DEVBUF

MALLOC_DEFINE(M_VLAN, VLANNAME, "802.1Q Virtual LAN Interface");

static LIST_HEAD(, ifvlan) ifv_list;

#if 0
/*
 * Locking: one lock is used to guard both the ifv_list and modification
 * to vlan data structures.  We are rather conservative here; probably
 * more than necessary.
 */
static struct mtx ifv_mtx;
#define	VLAN_LOCK_INIT()	mtx_init(&ifv_mtx, VLANNAME, NULL, MTX_DEF)
#define	VLAN_LOCK_DESTROY()	mtx_destroy(&ifv_mtx)
#define	VLAN_LOCK_ASSERT()	mtx_assert(&ifv_mtx, MA_OWNED)
#define	VLAN_LOCK()	mtx_lock(&ifv_mtx)
#define	VLAN_UNLOCK()	mtx_unlock(&ifv_mtx)
#else
#define	VLAN_LOCK_INIT()
#define	VLAN_LOCK_DESTROY()
#define	VLAN_LOCK_ASSERT()
#define	VLAN_LOCK()
#define	VLAN_UNLOCK()
#endif 0

static	int vlan_clone_create(struct if_clone *, int);
static	void vlan_clone_destroy(struct ifnet *);
static	int vlan_output(struct ifnet *ifp, struct mbuf *m);
static	void vlan_ifinit(void *foo);
static	int vlan_ioctl(struct ifnet *ifp, u_long cmd, void * addr);
static  int vlan_set_bpf_tap(struct ifnet * ifp, int mode,
			     bpf_callback_func * func);
static 	int vlan_attach_protocol(struct ifnet *ifp);
static	int vlan_detach_protocol(struct ifnet *ifp);
static 	int vlan_attach_filter(struct ifnet * ifp, u_long * filter_id);
static 	int vlan_detach_filter(u_long filter_id);
static	int vlan_setmulti(struct ifnet *ifp);
static	int vlan_unconfig(struct ifnet *ifp);
static	int vlan_config(struct ifvlan *ifv, struct ifnet *p, int tag);
static	int vlan_if_free(struct ifnet * ifp);

static struct if_clone vlan_cloner = IF_CLONE_INITIALIZER(VLANNAME,
							  vlan_clone_create, vlan_clone_destroy, 0, IF_MAXUNIT);

static	if_set_bpf_tap_func nop_if_bpf;
static	int nop_if_free(struct ifnet *);
static	int nop_if_ioctl(struct ifnet *, u_long, void *);
static	int nop_if_output(struct ifnet * ifp, struct mbuf * m);

static 	void interface_link_event(struct ifnet * ifp, u_long event_code);

static __inline__ void 
vlan_bpf_output(struct ifnet * ifp, struct mbuf * m, 
		bpf_callback_func func)
{
    if (func != NULL) {
	func(ifp, m);
    }
    return;
}

static __inline__ void 
vlan_bpf_input(struct ifnet * ifp, struct mbuf * m, 
	       bpf_callback_func func, char * frame_header,
	       int frame_header_len, int encap_len)
{
    if (func != NULL) {
	if (encap_len > 0) {
	    /* present the right header to bpf */
	    bcopy(frame_header, frame_header + encap_len, frame_header_len);
	}
	m->m_data -= frame_header_len;
	m->m_len += frame_header_len;
	func(ifp, m);
	m->m_data += frame_header_len;
	m->m_len -= frame_header_len;
	if (encap_len > 0) {
	    /* restore the header */
	    bcopy(frame_header + encap_len, frame_header, frame_header_len);
	}
    }
    return;
}

static struct ifaddr * 
ifaddr_byindex(unsigned int i)
{
    if (i > if_index || i == 0) {
	return (NULL);
    }
    return (ifnet_addrs[i - 1]);
}

/*
 * Program our multicast filter. What we're actually doing is
 * programming the multicast filter of the parent. This has the
 * side effect of causing the parent interface to receive multicast
 * traffic that it doesn't really want, which ends up being discarded
 * later by the upper protocol layers. Unfortunately, there's no way
 * to avoid this: there really is only one physical interface.
 */
static int
vlan_setmulti(struct ifnet *ifp)
{
    struct ifnet		*p;
    struct ifmultiaddr	*ifma, *rifma = NULL;
    struct ifvlan		*sc;
    struct vlan_mc_entry	*mc = NULL;
    struct sockaddr_dl	sdl;
    int			error;

    /* Find the parent. */
    sc = ifp->if_private;
    p = sc->ifv_p;
    if (p == NULL) {
	/* no parent, so no need to program the multicast filter */
	return (0);
    }

    bzero((char *)&sdl, sizeof sdl);
    sdl.sdl_len = sizeof sdl;
    sdl.sdl_family = AF_LINK;
    sdl.sdl_index = p->if_index;
    sdl.sdl_type = IFT_ETHER;
    sdl.sdl_alen = ETHER_ADDR_LEN;

    /* First, remove any existing filter entries. */
    while (SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) {
	mc = SLIST_FIRST(&sc->vlan_mc_listhead);
	bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
	error = if_delmulti(p, (struct sockaddr *)&sdl);
	if (error)
	    return(error);
	SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
	FREE(mc, M_VLAN);
    }

    /* Now program new ones. */
    LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
	if (ifma->ifma_addr->sa_family != AF_LINK)
	    continue;
	mc = _MALLOC(sizeof(struct vlan_mc_entry), M_VLAN, M_WAITOK);
	bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
	      (char *)&mc->mc_addr, ETHER_ADDR_LEN);
	SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
	bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
	      LLADDR(&sdl), ETHER_ADDR_LEN);
	error = if_addmulti(p, (struct sockaddr *)&sdl, &rifma);
	if (error)
	    return(error);
    }

    return(0);
}

#if 0
/*
 * VLAN support can be loaded as a module.  The only place in the
 * system that's intimately aware of this is ether_input.  We hook
 * into this code through vlan_input_p which is defined there and
 * set here.  Noone else in the system should be aware of this so
 * we use an explicit reference here.
 *
 * NB: Noone should ever need to check if vlan_input_p is null or
 *     not.  This is because interfaces have a count of the number
 *     of active vlans (if_nvlans) and this should never be bumped
 *     except by vlan_config--which is in this module so therefore
 *     the module must be loaded and vlan_input_p must be non-NULL.
 */
extern	void (*vlan_input_p)(struct ifnet *, struct mbuf *);

static int
vlan_modevent(module_t mod, int type, void *data) 
{ 

    switch (type) { 
    case MOD_LOAD: 
	LIST_INIT(&ifv_list);
	VLAN_LOCK_INIT();
	vlan_input_p = vlan_input;
	if_clone_attach(&vlan_cloner);
	break; 
    case MOD_UNLOAD: 
	if_clone_detach(&vlan_cloner);
	vlan_input_p = NULL;
	while (!LIST_EMPTY(&ifv_list))
	    vlan_clone_destroy(LIST_FIRST(&ifv_list)->ifv_ifp);
	VLAN_LOCK_DESTROY();
	break;
    } 
    return 0; 
} 

static moduledata_t vlan_mod = { 
    "if_vlan", 
    vlan_modevent, 
    0
}; 

DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);

#endif 0

static struct ifvlan * 
vlan_lookup_ifp_and_tag(struct ifnet * ifp, int tag)
{
    struct ifvlan * ifv;

    LIST_FOREACH(ifv, &ifv_list, ifv_list) {
	if (ifp == ifv->ifv_p && tag == ifv->ifv_tag) {
	    return (ifv);
	}
    }
    return (NULL);
}

static struct ifvlan * 
vlan_lookup_ifp(struct ifnet * ifp)
{
    struct ifvlan * ifv;

    LIST_FOREACH(ifv, &ifv_list, ifv_list) {
	if (ifp == ifv->ifv_p) {
	    return (ifv);
	}
    }
    return (NULL);
}

static void
vlan_clone_attach(void)
{
    if_clone_attach(&vlan_cloner);
    return;
}

static int
vlan_clone_create(struct if_clone *ifc, int unit)
{
    int error;
    struct ifvlan *ifv;
    struct ifnet *ifp;

    ifv = _MALLOC(sizeof(struct ifvlan), M_VLAN, M_WAITOK);
    bzero(ifv, sizeof(struct ifvlan));
    SLIST_INIT(&ifv->vlan_mc_listhead);

    /* use the interface name as the unique id for ifp recycle */
    if (snprintf(ifv->ifv_name, sizeof(ifv->ifv_name), "%s%d",
		 ifc->ifc_name, unit) >= sizeof(ifv->ifv_name)) {
	FREE(ifv, M_VLAN);
	return (EINVAL);
    }
    error = dlil_if_acquire(APPLE_IF_FAM_VLAN,
			    ifv->ifv_name,
			    strlen(ifv->ifv_name),
			    &ifp);
    if (error) {
	FREE(ifv, M_VLAN);
	return (error);
    }
    ifv->ifv_ifp = ifp;
    ifp->if_private = ifv;
    ifp->if_name = (char *)ifc->ifc_name;
    ifp->if_unit = unit;
    ifp->if_family = APPLE_IF_FAM_VLAN;

#if 0
    /* NB: flags are not set here */
    ifp->if_linkmib = &ifv->ifv_mib;
    ifp->if_linkmiblen = sizeof ifv->ifv_mib;
    /* NB: mtu is not set here */
#endif 0

    ifp->if_ioctl = vlan_ioctl;
    ifp->if_set_bpf_tap = vlan_set_bpf_tap;
    ifp->if_free = nop_if_free;
    ifp->if_output = nop_if_output;
    ifp->if_hwassist = 0;
    ifp->if_addrlen = ETHER_ADDR_LEN; /* XXX ethernet specific */
    ifp->if_baudrate = 0;
    ifp->if_type = IFT_L2VLAN;
    ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN;
    error = dlil_if_attach(ifp);
    if (error) {
	dlil_if_release(ifp);
	FREE(ifv, M_VLAN);
	return (error);
    }

    /* attach as ethernet */
    bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));

    VLAN_LOCK();
    LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
    VLAN_UNLOCK();

    return (0);
}

static void
vlan_remove(struct ifvlan * ifv)
{
    VLAN_LOCK_ASSERT();
    ifv->ifv_detaching = 1;
    vlan_unconfig(ifv->ifv_ifp);
    LIST_REMOVE(ifv, ifv_list);
    return;
}

static void
vlan_if_detach(struct ifnet * ifp)
{
    ifp->if_output = nop_if_output;
    ifp->if_ioctl = nop_if_ioctl;
    ifp->if_set_bpf_tap = &nop_if_bpf;
    if (dlil_if_detach(ifp) == DLIL_WAIT_FOR_FREE) {
	ifp->if_free = vlan_if_free;
    } else {
	vlan_if_free(ifp);
    }
    return;
}

static void
vlan_clone_destroy(struct ifnet *ifp)
{
    struct ifvlan *ifv = ifp->if_private;

    if (ifv == NULL || ifp->if_type != IFT_L2VLAN) {
	return;
    }
    VLAN_LOCK();
    if (ifv->ifv_detaching) {
	VLAN_UNLOCK();
	return;
    }
    vlan_remove(ifv);
    VLAN_UNLOCK();
    vlan_if_detach(ifp);
    return;
}

static int 
vlan_set_bpf_tap(struct ifnet * ifp, int mode, bpf_callback_func * func)
{
    struct ifvlan *ifv = ifp->if_private;

    switch (mode) {
        case BPF_TAP_DISABLE:
            ifv->ifv_bpf_input = ifv->ifv_bpf_output = NULL;
            break;

        case BPF_TAP_INPUT:
            ifv->ifv_bpf_input = func;
            break;

        case BPF_TAP_OUTPUT:
	    ifv->ifv_bpf_output = func;
            break;
        
        case BPF_TAP_INPUT_OUTPUT:
            ifv->ifv_bpf_input = ifv->ifv_bpf_output = func;
            break;
        default:
            break;
    }
    return 0;
}

static void
vlan_ifinit(void *foo)
{
    return;
}

static int
vlan_output(struct ifnet *ifp, struct mbuf *m)
{
    struct ifvlan *ifv;
    struct ifnet *p;
    struct ether_vlan_header *evl;
    int soft_vlan;

    ifv = ifp->if_private;
    p = ifv->ifv_p;
    if (p == NULL) {
	return (nop_if_output(ifp, m));
    }
    if (m == 0) {
	printf("%s: NULL output mbuf\n", ifv->ifv_name);
	return (EINVAL);
    }
    if ((m->m_flags & M_PKTHDR) == 0) {
	printf("%s: M_PKTHDR bit not set\n", ifv->ifv_name);
	m_freem(m);
	return (EINVAL);
    }
    ifp->if_obytes += m->m_pkthdr.len;
    ifp->if_opackets++;
    soft_vlan = (p->if_hwassist & IF_HWASSIST_VLAN_TAGGING) == 0;
    vlan_bpf_output(ifp, m, ifv->ifv_bpf_output);

    /* do not run parent's if_output() if the parent is not up */
    if ((p->if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) {
	m_freem(m);
	ifp->if_collisions++;
	return (0);
    }
    /*
     * If underlying interface can do VLAN tag insertion itself,
     * just pass the packet along. However, we need some way to
     * tell the interface where the packet came from so that it
     * knows how to find the VLAN tag to use.  We use a field in
     * the mbuf header to store the VLAN tag, and a bit in the
     * csum_flags field to mark the field as valid.
     */
    if (soft_vlan == 0) {
	m->m_pkthdr.csum_flags |= CSUM_VLAN_TAG_VALID;
	m->m_pkthdr.vlan_tag = ifv->ifv_tag;
    } else {
	M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
	if (m == NULL) {
	    printf("%s: unable to prepend VLAN header\n", 
		   ifv->ifv_name);
	    ifp->if_ierrors++;
	    return (0);
	}
	/* M_PREPEND takes care of m_len, m_pkthdr.len for us */
	if (m->m_len < sizeof(*evl)) {
	    m = m_pullup(m, sizeof(*evl));
	    if (m == NULL) {
		printf("%s: cannot pullup VLAN header\n",
		       ifv->ifv_name);
		ifp->if_ierrors++;
		return (0);
	    }
	}
		
	/*
	 * Transform the Ethernet header into an Ethernet header
	 * with 802.1Q encapsulation.
	 */
	bcopy(mtod(m, char *) + ifv->ifv_encaplen,
	      mtod(m, char *), ETHER_HDR_LEN);
	evl = mtod(m, struct ether_vlan_header *);
	evl->evl_proto = evl->evl_encap_proto;
	evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
	evl->evl_tag = htons(ifv->ifv_tag);
	m->m_pkthdr.len += ifv->ifv_encaplen;
    }
	
    /*
     * Send it, precisely as ether_output() would have.
     * We are already running at splimp.
     */
    return ((*p->if_output)(p, m));
}

extern int 
vlan_demux(struct ifnet * ifp, struct mbuf * m, 
	   char * frame_header, struct if_proto * * proto)
{
    register struct ether_header *eh = (struct ether_header *)frame_header;
    struct ether_vlan_header *evl;
    struct ifvlan *ifv = NULL;
    int soft_vlan = 0;
    u_int tag;

    if (m->m_pkthdr.csum_flags & CSUM_VLAN_TAG_VALID) {
	/*
	 * Packet is tagged, m contains a normal
	 * Ethernet frame; the tag is stored out-of-band.
	 */
	m->m_pkthdr.csum_flags &= ~CSUM_VLAN_TAG_VALID;
	tag = EVL_VLANOFTAG(m->m_pkthdr.vlan_tag);
	m->m_pkthdr.vlan_tag = 0;
    } else {
	soft_vlan = 1;

	switch (ifp->if_type) {
	case IFT_ETHER:
	    if (m->m_len < ETHER_VLAN_ENCAP_LEN) {
		m_freem(m);
		return (EJUSTRETURN);
	    }
	    evl = (struct ether_vlan_header *)frame_header;
	    if (ntohs(evl->evl_proto) == ETHERTYPE_VLAN) {
		/* don't allow VLAN within VLAN */
		m_freem(m);
		return (EJUSTRETURN);
	    }
	    tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));

	    /*
	     * Restore the original ethertype.  We'll remove
	     * the encapsulation after we've found the vlan
	     * interface corresponding to the tag.
	     */
	    evl->evl_encap_proto = evl->evl_proto;
	    break;
	default:
	    printf("vlan_demux: unsupported if type %u", 
		   ifp->if_type);
	    m_freem(m);
	    return (EJUSTRETURN);
	    break;
	}
    }
    if (tag != 0) {
	if (ifp->if_nvlans == 0) {
	    /* don't bother looking through the VLAN list */
	    m_freem(m);
	    ifp->if_noproto++;
	    return (EJUSTRETURN);
	}
	VLAN_LOCK();
	ifv = vlan_lookup_ifp_and_tag(ifp, tag);
	if (ifv == NULL || (ifv->ifv_ifp->if_flags & IFF_UP) == 0) {
	    VLAN_UNLOCK();
	    m_freem(m);
	    ifp->if_noproto++;
	    return (EJUSTRETURN);
	}
	VLAN_UNLOCK();		/* XXX extend below? */
    }
    if (soft_vlan) {
	/*
	 * Packet had an in-line encapsulation header;
	 * remove it.  The original header has already
	 * been fixed up above.
	 */
	m->m_len -= ETHER_VLAN_ENCAP_LEN;
	m->m_data += ETHER_VLAN_ENCAP_LEN;
	m->m_pkthdr.len -= ETHER_VLAN_ENCAP_LEN;
	m->m_pkthdr.csum_flags = 0; /* can't trust hardware checksum */
    }
    if (tag != 0) {
	/* we found a vlan interface above, so send it up */
	m->m_pkthdr.rcvif = ifv->ifv_ifp;
	ifv->ifv_ifp->if_ipackets++;
	ifv->ifv_ifp->if_ibytes += m->m_pkthdr.len;

	vlan_bpf_input(ifv->ifv_ifp, m, ifv->ifv_bpf_input, frame_header,
		       ETHER_HDR_LEN, soft_vlan ? ETHER_VLAN_ENCAP_LEN : 0);

	/* Pass it back through the parent's demux routine. */
	return ((*ifp->if_demux)(ifv->ifv_ifp, m, frame_header, proto));
    }
    /* Pass it back through calling demux routine. */
    return ((*ifp->if_demux)(ifp, m, frame_header, proto));
}

static int
vlan_config(struct ifvlan *ifv, struct ifnet *p, int tag)
{
    struct ifnet * ifp;
    struct ifaddr *ifa1, *ifa2;
    struct sockaddr_dl *sdl1, *sdl2;
    int supports_vlan_mtu = 0;

    VLAN_LOCK_ASSERT();
    if (p->if_data.ifi_type != IFT_ETHER)
	return EPROTONOSUPPORT;
    if (ifv->ifv_p != NULL || ifv->ifv_detaching) {
	return EBUSY;
    }
    if (vlan_lookup_ifp_and_tag(p, tag) != NULL) {
	/* already a VLAN with that tag on this interface */
	return (EADDRINUSE);
    }
    ifp = ifv->ifv_ifp;
    ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
    ifv->ifv_mintu = ETHERMIN;
    ifv->ifv_flags = 0;

    /*
     * If the parent supports the VLAN_MTU capability,
     * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
     * enable it.
     */
    if (p->if_hwassist & (IF_HWASSIST_VLAN_MTU | IF_HWASSIST_VLAN_TAGGING)) {
	supports_vlan_mtu = 1;
    }
    if (p->if_nvlans == 0) {
	u_long	dltag;
	u_long	filter_id;
	int 	error;

	/* attach our VLAN "interface filter" to the interface */
	error = vlan_attach_filter(p, &filter_id);
	if (error) {
	    return (error);
	}

	/* attach our VLAN "protocol" to the interface */
	error = vlan_attach_protocol(p);
	if (error) {
	    (void)vlan_detach_filter(filter_id);
	    return (error);
	}
	ifv->ifv_filter_id = filter_id;
	ifv->ifv_filter_valid = TRUE;
#if 0
	if (supports_vlan_mtu) {
	    /*
	     * Enable Tx/Rx of VLAN-sized frames.
	     */
	    p->if_capenable |= IFCAP_VLAN_MTU;
	    if (p->if_flags & IFF_UP) {
		struct ifreq ifr;
		int error;
		
		ifr.ifr_flags = p->if_flags;
		error = (*p->if_ioctl)(p, SIOCSIFFLAGS,
				       (caddr_t) &ifr);
		if (error) {
		    if (p->if_nvlans == 0)
			p->if_capenable &= ~IFCAP_VLAN_MTU;
		    return (error);
		}
	    }
	}
#endif 0
    } else {
	struct ifvlan * 	other_ifv;

	other_ifv = vlan_lookup_ifp(p);
	if (other_ifv == NULL) {
	    printf("vlan: other_ifv can't be NULL\n");
	    return (EINVAL);
	}
	ifv->ifv_filter_id = other_ifv->ifv_filter_id;
	ifv->ifv_filter_valid = TRUE;
    }
    p->if_nvlans++;
    if (supports_vlan_mtu) {
	ifv->ifv_mtufudge = 0;
    } else {
	/*
	 * Fudge the MTU by the encapsulation size.  This
	 * makes us incompatible with strictly compliant
	 * 802.1Q implementations, but allows us to use
	 * the feature with other NetBSD implementations,
	 * which might still be useful.
	 */
	ifv->ifv_mtufudge = ifv->ifv_encaplen;
    }

    ifv->ifv_p = p;
    ifp->if_mtu = p->if_mtu - ifv->ifv_mtufudge;
    /*
     * Copy only a selected subset of flags from the parent.
     * Other flags are none of our business.
     */
    ifp->if_flags |= (p->if_flags &
		      (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX));
    /*
     * If the parent interface can do hardware-assisted
     * VLAN encapsulation, then propagate its hardware-
     * assisted checksumming flags.
     */
    if (p->if_hwassist & IF_HWASSIST_VLAN_TAGGING) {
	ifp->if_hwassist |= IF_HWASSIST_CSUM_FLAGS(p->if_hwassist);
    }
    /*
     * Set up our ``Ethernet address'' to reflect the underlying
     * physical interface's.
     */
    ifa1 = ifaddr_byindex(ifp->if_index);
    ifa2 = ifaddr_byindex(p->if_index);
    sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
    sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
    sdl1->sdl_type = IFT_ETHER;
    sdl1->sdl_alen = ETHER_ADDR_LEN;
    bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
    bcopy(LLADDR(sdl2), IFP2AC(ifp)->ac_enaddr, ETHER_ADDR_LEN);

    /*
     * Configure multicast addresses that may already be
     * joined on the vlan device.
     */
    (void)vlan_setmulti(ifp);
    ifp->if_output = vlan_output;
    ifv->ifv_tag = tag;

    return 0;
}

static void
vlan_link_event(struct ifnet * ifp, struct ifnet * p)
{
    struct ifmediareq ifmr;

    /* generate a link event based on the state of the underlying interface */
    bzero(&ifmr, sizeof(ifmr));
    snprintf(ifmr.ifm_name, sizeof(ifmr.ifm_name),
	     "%s%d", p->if_name, p->if_unit);
    if ((*p->if_ioctl)(p, SIOCGIFMEDIA, (caddr_t)&ifmr) == 0
	&& ifmr.ifm_count > 0 && ifmr.ifm_status & IFM_AVALID) {
	u_long	event;
	
	event = (ifmr.ifm_status & IFM_ACTIVE)
	    ? KEV_DL_LINK_ON : KEV_DL_LINK_OFF;
	interface_link_event(ifp, event);
    }
    return;
}

static int
vlan_unconfig(struct ifnet *ifp)
{
    struct ifaddr *ifa;
    struct sockaddr_dl *sdl;
    struct vlan_mc_entry *mc;
    struct ifvlan *ifv;
    struct ifnet *p;
    int error;

    VLAN_LOCK_ASSERT();

    ifv = ifp->if_private;

    /* Disconnect from parent. */
    p = ifv->ifv_p;
    ifv->ifv_p = NULL;

    if (p != NULL) {
	struct sockaddr_dl sdl;

	/*
	 * Since the interface is being unconfigured, we need to
	 * empty the list of multicast groups that we may have joined
	 * while we were alive from the parent's list.
	 */
	bzero((char *)&sdl, sizeof sdl);
	sdl.sdl_len = sizeof sdl;
	sdl.sdl_family = AF_LINK;
	sdl.sdl_index = p->if_index;
	sdl.sdl_type = IFT_ETHER;
	sdl.sdl_alen = ETHER_ADDR_LEN;

	while (SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) {
	    mc = SLIST_FIRST(&ifv->vlan_mc_listhead);
	    bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
	    error = if_delmulti(p, (struct sockaddr *)&sdl);
	    if (error) {
		printf("vlan_unconfig: if_delmulti %s failed, %d\n", 
		       ifv->ifv_name, error);
	    }
	    SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
	    FREE(mc, M_VLAN);
	}
	p->if_nvlans--;
	if (p->if_nvlans == 0) {
	    /* detach our VLAN "protocol" from the interface */
	    if (ifv->ifv_filter_valid) {
		(void)vlan_detach_filter(ifv->ifv_filter_id);
	    }
	    (void)vlan_detach_protocol(p);
#if 0
	    /*
	     * Disable Tx/Rx of VLAN-sized frames.
	     */
	    p->if_capenable &= ~IFCAP_VLAN_MTU;
	    if (p->if_flags & IFF_UP) {
		struct ifreq ifr;
		
		ifr.ifr_flags = p->if_flags;
		(*p->if_ioctl)(p, SIOCSIFFLAGS, (caddr_t) &ifr);
	    }
#endif 0
	}
    }

    /* return to the state we were in before SETVLAN */
    ifp->if_mtu = 0;
    ifp->if_flags &= ~(IFF_BROADCAST | IFF_MULTICAST 
		       | IFF_SIMPLEX | IFF_RUNNING);
    ifv->ifv_ifp->if_hwassist = 0;
    ifv->ifv_flags = 0;
    ifv->ifv_ifp->if_output = nop_if_output;
    ifv->ifv_mtufudge = 0;
    ifv->ifv_filter_valid = FALSE;

    /* Clear our MAC address. */
    ifa = ifaddr_byindex(ifv->ifv_ifp->if_index);
    sdl = (struct sockaddr_dl *)(ifa->ifa_addr);
    sdl->sdl_type = IFT_L2VLAN;
    sdl->sdl_alen = 0;
    bzero(LLADDR(sdl), ETHER_ADDR_LEN);
    bzero(IFP2AC(ifv->ifv_ifp)->ac_enaddr, ETHER_ADDR_LEN);

    /* send a link down event */
    if (p != NULL) {
	interface_link_event(ifv->ifv_ifp, KEV_DL_LINK_OFF);
    }
    return 0;
}

static int
vlan_set_promisc(struct ifnet *ifp)
{
    struct ifvlan *ifv = ifp->if_private;
    int error = 0;

    if ((ifp->if_flags & IFF_PROMISC) != 0) {
	if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
	    error = ifpromisc(ifv->ifv_p, 1);
	    if (error == 0)
		ifv->ifv_flags |= IFVF_PROMISC;
	}
    } else {
	if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
	    error = ifpromisc(ifv->ifv_p, 0);
	    if (error == 0)
		ifv->ifv_flags &= ~IFVF_PROMISC;
	}
    }

    return (error);
}

static int
vlan_ioctl(struct ifnet *ifp, u_long cmd, void * data)
{
    struct ifaddr *ifa;
    struct ifnet *p;
    struct ifreq *ifr;
    struct ifvlan *ifv;
    struct vlanreq vlr;
    int error = 0;

    ifr = (struct ifreq *)data;
    ifa = (struct ifaddr *)data;
    ifv = (struct ifvlan *)ifp->if_private;

    switch (cmd) {
    case SIOCSIFADDR:
	ifp->if_flags |= IFF_UP;
	break;

    case SIOCGIFMEDIA:
	VLAN_LOCK();
	if (ifv->ifv_p != NULL) {
	    error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p,
					    SIOCGIFMEDIA, data);
	    VLAN_UNLOCK();
	    /* Limit the result to the parent's current config. */
	    if (error == 0) {
		struct ifmediareq *ifmr;

		ifmr = (struct ifmediareq *) data;
		if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) {
		    ifmr->ifm_count = 1;
		    error = copyout(&ifmr->ifm_current,
				    ifmr->ifm_ulist, 
				    sizeof(int));
		}
	    }
	} else {
	    struct ifmediareq *ifmr;
	    VLAN_UNLOCK();
	    
	    ifmr = (struct ifmediareq *) data;
	    ifmr->ifm_current = 0;
	    ifmr->ifm_mask = 0;
	    ifmr->ifm_status = IFM_AVALID;
	    ifmr->ifm_active = 0;
	    ifmr->ifm_count = 1;
	    if (ifmr->ifm_ulist) {
		error = copyout(&ifmr->ifm_current,
				ifmr->ifm_ulist, 
				sizeof(int));
	    }
	    error = 0;
	}
	break;

    case SIOCSIFMEDIA:
	error = EINVAL;
	break;

    case SIOCSIFMTU:
	/*
	 * Set the interface MTU.
	 */
	VLAN_LOCK();
	if (ifv->ifv_p != NULL) {
	    if (ifr->ifr_mtu > (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge)
		|| ifr->ifr_mtu < (ifv->ifv_mintu - ifv->ifv_mtufudge)) {
		error = EINVAL;
	    } else {
		ifp->if_mtu = ifr->ifr_mtu;
	    }
	} else {
	    error = EINVAL;
	}
	VLAN_UNLOCK();
	break;

    case SIOCSETVLAN:
	error = copyin(ifr->ifr_data, &vlr, sizeof(vlr));
	if (error)
	    break;
	if (vlr.vlr_parent[0] == '\0') {
	    VLAN_LOCK();
	    vlan_unconfig(ifp);
#if 0
	    if (ifp->if_flags & IFF_UP)
		if_down(ifp);
	    ifp->if_flags &= ~IFF_RUNNING;
#endif 0
	    VLAN_UNLOCK();
	    break;
	}
	p = ifunit(vlr.vlr_parent);
	if (p == 0) {
	    error = ENOENT;
	    break;
	}
	/*
	 * Don't let the caller set up a VLAN tag with
	 * anything except VLID bits.
	 */
	if (vlr.vlr_tag & ~EVL_VLID_MASK) {
	    error = EINVAL;
	    break;
	}
	VLAN_LOCK();
	error = vlan_config(ifv, p, vlr.vlr_tag);
	if (error) {
	    VLAN_UNLOCK();
	    break;
	}
	ifp->if_flags |= IFF_RUNNING;
	VLAN_UNLOCK();

	/* Update promiscuous mode, if necessary. */
	vlan_set_promisc(ifp);

	/* generate a link event */
	vlan_link_event(ifp, p);
	break;
		
    case SIOCGETVLAN:
	bzero(&vlr, sizeof vlr);
	VLAN_LOCK();
	if (ifv->ifv_p != NULL) {
	    snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
		     "%s%d", ifv->ifv_p->if_name, 
		     ifv->ifv_p->if_unit);
	    vlr.vlr_tag = ifv->ifv_tag;
	}
	VLAN_UNLOCK();
	error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
	break;
		
    case SIOCSIFFLAGS:
	/*
	 * For promiscuous mode, we enable promiscuous mode on
	 * the parent if we need promiscuous on the VLAN interface.
	 */
	if (ifv->ifv_p != NULL)
	    error = vlan_set_promisc(ifp);
	break;

    case SIOCADDMULTI:
    case SIOCDELMULTI:
	error = vlan_setmulti(ifp);
	break;
    default:
	error = EOPNOTSUPP;
    }
    return error;
}

static int 
nop_if_ioctl(struct ifnet * ifp, u_long cmd, void * data)
{
    return EOPNOTSUPP;
}

static int 
nop_if_bpf(struct ifnet *ifp, int mode, bpf_callback_func * func)
{
    return ENODEV;
}

static int 
nop_if_free(struct ifnet * ifp)
{
    return 0;
}

static int 
nop_if_output(struct ifnet * ifp, struct mbuf * m)
{
    if (m != NULL) {
	m_freem_list(m);
    }
    return 0;
}

static int 
vlan_if_free(struct ifnet * ifp)
{
    struct ifvlan *ifv;

    if (ifp == NULL) {
	return 0;
    }
    ifv = (struct ifvlan *)ifp->if_private;
    if (ifv == NULL) {
	return 0;
    }
    ifp->if_private = NULL;
    dlil_if_release(ifp);
    FREE(ifv, M_VLAN);
    return 0;
}

/*
 * Function: vlan_if_filter_detach
 * Purpose:
 *   Destroy all vlan interfaces that refer to the interface
 */
static int
vlan_if_filter_detach(caddr_t cookie)
{
    struct ifnet * 	ifp;
    struct ifvlan *	ifv;
    struct ifnet * 	p = (struct ifnet *)cookie;

    VLAN_LOCK();
    while (TRUE) {
	ifv = vlan_lookup_ifp(p);
	if (ifv == NULL) {
	    break;
	}
	if (ifv->ifv_detaching) {
	    continue;
	}
	/* make sure we don't invoke vlan_detach_filter */
	ifv->ifv_filter_valid = FALSE;
	vlan_remove(ifv);
	ifp = ifv->ifv_ifp;
	VLAN_UNLOCK();
	vlan_if_detach(ifp);
	VLAN_LOCK();
    }
    VLAN_UNLOCK();
    return (0);
}

/*
 * Function: vlan_attach_filter
 * Purpose:
 *   We attach an interface filter to detect when the underlying interface
 *   goes away.  We are forced to do that because dlil does not call our
 *   protocol's dl_event function for KEV_DL_IF_DETACHING.
 */

static int
vlan_attach_filter(struct ifnet * ifp, u_long * filter_id)
{
    int				error;
    struct dlil_if_flt_str	filt;

    bzero(&filt, sizeof(filt));
    filt.filter_detach = vlan_if_filter_detach;
    filt.cookie = (caddr_t)ifp;
    error = dlil_attach_interface_filter(ifp, &filt, filter_id, 
					 DLIL_LAST_FILTER);
    if (error) {
	printf("vlan: dlil_attach_interface_filter(%s%d) failed, %d\n",
	       ifp->if_name, ifp->if_unit, error);
    }
    return (error);
}

/*
 * Function: vlan_detach_filter
 * Purpose:
 *   Remove our interface filter.
 */
static int
vlan_detach_filter(u_long filter_id)
{
    int 	error;

    error = dlil_detach_filter(filter_id);
    if (error) {
	printf("vlan: dlil_detach_filter failed, %d\n", error);
    }
    return (error);
}

/*
 * Function: vlan_proto_input
 * Purpose:
 *   This function is never called.  We aren't allowed to leave the
 *   function pointer NULL, so this function simply free's the mbuf.
 */
static int
vlan_proto_input(m, frame_header, ifp, dl_tag, sync_ok)
    struct mbuf  *m;
    char         *frame_header;
    struct ifnet *ifp;
    u_long	     dl_tag;
    int          sync_ok;
{
    m_freem(m);
    return (EJUSTRETURN);
}

static struct ifnet *
find_if_name_unit(const char * if_name, int unit)
{
    struct ifnet * ifp;
    
    TAILQ_FOREACH(ifp, &ifnet, if_link) {
	if (strcmp(if_name, ifp->if_name) == 0 && unit == ifp->if_unit) {
	    return (ifp);
	}
    }
    return (ifp);
}

static void
interface_link_event(struct ifnet * ifp, u_long event_code)
{
    struct {
	struct kern_event_msg	header;
	u_long			unit;
	char			if_name[IFNAMSIZ];
    } event;

    event.header.total_size    = sizeof(event);
    event.header.vendor_code   = KEV_VENDOR_APPLE;
    event.header.kev_class     = KEV_NETWORK_CLASS;
    event.header.kev_subclass  = KEV_DL_SUBCLASS;
    event.header.event_code    = event_code;
    event.header.event_data[0] = ifp->if_family;
    event.unit                 = (u_long) ifp->if_unit;
    strncpy(event.if_name, ifp->if_name, IFNAMSIZ);
    dlil_event(ifp, &event.header);
    return;
}

static void
parent_link_event(struct ifnet * p, u_long event_code)
{
    struct ifvlan * ifv;

    LIST_FOREACH(ifv, &ifv_list, ifv_list) {
	if (p == ifv->ifv_p) {
	    interface_link_event(ifv->ifv_ifp, event_code);
	}
    }
    return;

}

/* 
 * Function: vlan_dl_event
 * Purpose:
 *   Process DLIL events that interest us.  Currently, that is
 *   just the interface UP and DOWN.  Ideally, this would also
 *   include the KEV_DL_IF_DETACH{ING} messages, which would eliminate
 *   the need for an interface filter.
 */
static int
vlan_dl_event(struct kern_event_msg * event, u_long dl_tag)
{
    struct ifnet *		p;
    struct net_event_data *	net_event;

    if (event->vendor_code != KEV_VENDOR_APPLE
	|| event->kev_class != KEV_NETWORK_CLASS
	|| event->kev_subclass != KEV_DL_SUBCLASS) {
	goto done;
    }
    net_event = (struct net_event_data *)(event->event_data);
    switch (event->event_code) {
    case KEV_DL_LINK_OFF:
    case KEV_DL_LINK_ON:
	p = find_if_name_unit(net_event->if_name, net_event->if_unit);
	if (p != NULL) {
	    parent_link_event(p, event->event_code);
	}
	break;
#if 0
    case KEV_DL_IF_DETACHING:
    case KEV_DL_IF_DETACHED:
	/* we don't get these, unfortunately */
	break;
#endif 0
    default:
	break;
    }

 done:
    return (0);
}

/*
 * Function: vlan_attach_protocol
 * Purpose:
 *   Attach a DLIL protocol to the interface, using the ETHERTYPE_VLAN
 *   demux ether type.  We're not a real protocol, we'll never receive
 *   any packets because they're intercepted by ether_demux before 
 *   our input routine would be called.
 *
 *   The reasons for attaching a protocol to the interface are:
 *   1) add a protocol reference to the interface so that the underlying
 *      interface automatically gets marked up while we're attached
 *   2) receive link status events which we can propagate to our
 *      VLAN interfaces.
 */
static int
vlan_attach_protocol(struct ifnet *ifp)
{
    struct dlil_demux_desc      desc;
    u_long			dl_tag;
    u_short 			en_native = ETHERTYPE_VLAN;
    int  			error;
    int 			i;
    struct dlil_proto_reg_str   reg;

    TAILQ_INIT(&reg.demux_desc_head);
    desc.type = DLIL_DESC_RAW;
    desc.variants.bitmask.proto_id_length = 0;
    desc.variants.bitmask.proto_id = 0;
    desc.variants.bitmask.proto_id_mask = 0;
    desc.native_type = (char *) &en_native;
    TAILQ_INSERT_TAIL(&reg.demux_desc_head, &desc, next);
    reg.interface_family = ifp->if_family;
    reg.unit_number      = ifp->if_unit;
    reg.input            = vlan_proto_input;
    reg.pre_output       = 0;
    reg.event            = vlan_dl_event;
    reg.offer            = 0;
    reg.ioctl            = 0;
    reg.default_proto    = 0;
    reg.protocol_family  = VLAN_PROTO_FAMILY;

    error = dlil_attach_protocol(&reg, &dl_tag);
    if (error) {
	printf("vlan_proto_attach(%s%d) dlil_attach_protocol failed, %d\n",
	       ifp->if_name, ifp->if_unit, error);
    }
    return (error);
}

/*
 * Function: vlan_detach_protocol
 * Purpose:
 *   Detach our DLIL protocol from an interface
 */
static int
vlan_detach_protocol(struct ifnet *ifp)
{
    u_long	dl_tag;
    int         error;

    error = dlil_find_dltag(ifp->if_family, ifp->if_unit, 
			    VLAN_PROTO_FAMILY, &dl_tag);
    if (error) {
	printf("vlan_proto_detach(%s%d) dlil_find_dltag failed, %d\n",
	       ifp->if_name, ifp->if_unit, error);
    } else {
        error = dlil_detach_protocol(dl_tag);
	if (error) {
	    printf("vlan_proto_detach(%s%d) dlil_detach_protocol failed, %d\n",
		   ifp->if_name, ifp->if_unit, error);
	}
    }
    return (error);
}

/*
 * DLIL interface family functions
 *   We use the ethernet dlil functions, since that's all we support.
 *   If we wanted to handle multiple LAN types (tokenring, etc.), we'd
 *   call the appropriate routines for that LAN type instead of hard-coding
 *   ethernet.
 */
extern int ether_add_if(struct ifnet *ifp);
extern int ether_del_if(struct ifnet *ifp);
extern int ether_init_if(struct ifnet *ifp);
extern int ether_add_proto(struct ddesc_head_str *desc_head, 
			   struct if_proto *proto, u_long dl_tag);
extern int ether_del_proto(struct if_proto *proto, u_long dl_tag);
extern int ether_ifmod_ioctl(struct ifnet *ifp, u_long command,
			     caddr_t data);
extern int ether_del_proto(struct if_proto *proto, u_long dl_tag);
extern int ether_add_proto(struct ddesc_head_str *desc_head, struct if_proto *proto, u_long dl_tag);

extern int ether_attach_inet(struct ifnet *ifp, u_long *dl_tag);
extern int ether_detach_inet(struct ifnet *ifp, u_long dl_tag);
extern int ether_attach_inet6(struct ifnet *ifp, u_long *dl_tag);
extern int ether_detach_inet6(struct ifnet *ifp, u_long dl_tag);

static int
vlan_attach_inet(struct ifnet *ifp, u_long *dl_tag)
{
    return (ether_attach_inet(ifp, dl_tag));
}

static int
vlan_detach_inet(struct ifnet *ifp, u_long dl_tag)
{
    return (ether_detach_inet(ifp, dl_tag));
}

static int
vlan_attach_inet6(struct ifnet *ifp, u_long *dl_tag)
{
    return (ether_attach_inet6(ifp, dl_tag));
}

static int
vlan_detach_inet6(struct ifnet *ifp, u_long dl_tag)
{
    return (ether_detach_inet6(ifp, dl_tag));
}

static int
vlan_add_if(struct ifnet *ifp)
{
    return (ether_add_if(ifp));
}

static int
vlan_del_if(struct ifnet *ifp)
{
    return (ether_del_if(ifp));
}

static int
vlan_init_if(struct ifnet *ifp)
{
    return (0);
}

static int
vlan_shutdown()
{
    return 0;
}

__private_extern__ int
vlan_family_init()
{
    int  i, error=0;
    struct dlil_ifmod_reg_str  ifmod_reg;
    struct dlil_protomod_reg_str vlan_protoreg;

#if 0
    /* VLAN family is built-in, called from ether_family_init */
    thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
#endif 0

    bzero(&ifmod_reg, sizeof(ifmod_reg));
    ifmod_reg.add_if = vlan_add_if;
    ifmod_reg.del_if = vlan_del_if;
    ifmod_reg.init_if = vlan_init_if;
    ifmod_reg.add_proto = ether_add_proto;
    ifmod_reg.del_proto = ether_del_proto;
    ifmod_reg.ifmod_ioctl = ether_ifmod_ioctl;
    ifmod_reg.shutdown    = vlan_shutdown;

    if (dlil_reg_if_modules(APPLE_IF_FAM_VLAN, &ifmod_reg)) {
	printf("WARNING: vlan_family_init -- "
	       "Can't register if family modules\n");
	error = EIO;
	goto done;
    }

    /* Register protocol registration functions */
    bzero(&vlan_protoreg, sizeof(vlan_protoreg));
    vlan_protoreg.attach_proto = vlan_attach_inet;
    vlan_protoreg.detach_proto = vlan_detach_inet;
	
    if (error = dlil_reg_proto_module(PF_INET, APPLE_IF_FAM_VLAN, 
				      &vlan_protoreg) != 0) {
	kprintf("dlil_reg_proto_module failed for AF_INET6 error=%d\n",
		error);
	goto done;
    }
    vlan_protoreg.attach_proto = vlan_attach_inet6;
    vlan_protoreg.detach_proto = vlan_detach_inet6;
	
    if (error = dlil_reg_proto_module(PF_INET6, APPLE_IF_FAM_VLAN, 
				      &vlan_protoreg) != 0) {
	kprintf("dlil_reg_proto_module failed for AF_INET6 error=%d\n",
		error);
	goto done;
    }
    vlan_clone_attach();

 done:
#if 0
    thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
#endif 0
    return (error);
}