summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/if_vlan.h1
-rw-r--r--net/8021q/vlan.c9
-rw-r--r--net/8021q/vlan_dev.c6
-rw-r--r--net/8021q/vlan_netlink.c3
4 files changed, 14 insertions, 5 deletions
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
index 153f6b9e722c..3d870fda8c4f 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -339,6 +339,7 @@ enum vlan_ioctl_cmds {
enum vlan_flags {
VLAN_FLAG_REORDER_HDR = 0x1,
VLAN_FLAG_GVRP = 0x2,
+ VLAN_FLAG_LOOSE_BINDING = 0x4,
};
enum vlan_name_types {
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index 1483243edf14..225aa2fac0e3 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -431,6 +431,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
struct vlan_group *grp;
int i, flgs;
struct net_device *vlandev;
+ struct vlan_dev_info *vlan;
LIST_HEAD(list);
if (is_vlan_dev(dev))
@@ -507,7 +508,9 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
if (!(flgs & IFF_UP))
continue;
- dev_change_flags(vlandev, flgs & ~IFF_UP);
+ vlan = vlan_dev_info(vlandev);
+ if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING))
+ dev_change_flags(vlandev, flgs & ~IFF_UP);
vlan_transfer_operstate(dev, vlandev);
}
break;
@@ -523,7 +526,9 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
if (flgs & IFF_UP)
continue;
- dev_change_flags(vlandev, flgs | IFF_UP);
+ vlan = vlan_dev_info(vlandev);
+ if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING))
+ dev_change_flags(vlandev, flgs | IFF_UP);
vlan_transfer_operstate(dev, vlandev);
}
break;
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index de0dc6bacbe8..b7889782047e 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -431,7 +431,8 @@ int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask)
struct vlan_dev_info *vlan = vlan_dev_info(dev);
u32 old_flags = vlan->flags;
- if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP))
+ if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP |
+ VLAN_FLAG_LOOSE_BINDING))
return -EINVAL;
vlan->flags = (old_flags & ~mask) | (flags & mask);
@@ -456,7 +457,8 @@ static int vlan_dev_open(struct net_device *dev)
struct net_device *real_dev = vlan->real_dev;
int err;
- if (!(real_dev->flags & IFF_UP))
+ if (!(real_dev->flags & IFF_UP) &&
+ !(vlan->flags & VLAN_FLAG_LOOSE_BINDING))
return -ENETDOWN;
if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) {
diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c
index 3c9cf6a8e7fb..ddc105734af7 100644
--- a/net/8021q/vlan_netlink.c
+++ b/net/8021q/vlan_netlink.c
@@ -60,7 +60,8 @@ static int vlan_validate(struct nlattr *tb[], struct nlattr *data[])
if (data[IFLA_VLAN_FLAGS]) {
flags = nla_data(data[IFLA_VLAN_FLAGS]);
if ((flags->flags & flags->mask) &
- ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP))
+ ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP |
+ VLAN_FLAG_LOOSE_BINDING))
return -EINVAL;
}