提交 f350a0a8 编写于 作者: J Jiri Pirko 提交者: David S. Miller

bridge: use rx_handler_data pointer to store net_bridge_port pointer

Register net_bridge_port pointer as rx_handler data pointer. As br_port is
removed from struct net_device, another netdev priv_flag is added to indicate
the device serves as a bridge port. Also rcuized pointers are now correctly
dereferenced in br_fdb.c and in netfilter parts.
Signed-off-by: NJiri Pirko <jpirko@redhat.com>
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
上级 a35e2c1b
...@@ -5718,7 +5718,7 @@ static void dev_set_promiscuous(struct net_device *dev, struct dev_priv *priv, ...@@ -5718,7 +5718,7 @@ static void dev_set_promiscuous(struct net_device *dev, struct dev_priv *priv,
* from the bridge. * from the bridge.
*/ */
if ((hw->features & STP_SUPPORT) && !promiscuous && if ((hw->features & STP_SUPPORT) && !promiscuous &&
dev->br_port) { (dev->priv_flags & IFF_BRIDGE_PORT)) {
struct ksz_switch *sw = hw->ksz_switch; struct ksz_switch *sw = hw->ksz_switch;
int port = priv->port.first_port; int port = priv->port.first_port;
......
...@@ -71,7 +71,7 @@ static int is_valid_iface(struct net_device *net_dev) ...@@ -71,7 +71,7 @@ static int is_valid_iface(struct net_device *net_dev)
#endif #endif
/* Device is being bridged */ /* Device is being bridged */
/* if (net_dev->br_port != NULL) /* if (net_dev->priv_flags & IFF_BRIDGE_PORT)
return 0; */ return 0; */
return 1; return 1;
......
...@@ -74,6 +74,7 @@ ...@@ -74,6 +74,7 @@
#define IFF_IN_NETPOLL 0x1000 /* whether we are processing netpoll */ #define IFF_IN_NETPOLL 0x1000 /* whether we are processing netpoll */
#define IFF_DISABLE_NETPOLL 0x2000 /* disable netpoll at run-time */ #define IFF_DISABLE_NETPOLL 0x2000 /* disable netpoll at run-time */
#define IFF_MACVLAN_PORT 0x4000 /* device used as macvlan port */ #define IFF_MACVLAN_PORT 0x4000 /* device used as macvlan port */
#define IFF_BRIDGE_PORT 0x8000 /* device used as bridge port */
#define IF_GET_IFACE 0x0001 /* for querying only */ #define IF_GET_IFACE 0x0001 /* for querying only */
#define IF_GET_PROTO 0x0002 #define IF_GET_PROTO 0x0002
......
...@@ -1047,8 +1047,6 @@ struct net_device { ...@@ -1047,8 +1047,6 @@ struct net_device {
/* mid-layer private */ /* mid-layer private */
void *ml_priv; void *ml_priv;
/* bridge stuff */
struct net_bridge_port *br_port;
/* GARP */ /* GARP */
struct garp_port *garp_port; struct garp_port *garp_port;
......
...@@ -242,11 +242,11 @@ int br_fdb_test_addr(struct net_device *dev, unsigned char *addr) ...@@ -242,11 +242,11 @@ int br_fdb_test_addr(struct net_device *dev, unsigned char *addr)
struct net_bridge_fdb_entry *fdb; struct net_bridge_fdb_entry *fdb;
int ret; int ret;
if (!dev->br_port) if (!br_port_exists(dev))
return 0; return 0;
rcu_read_lock(); rcu_read_lock();
fdb = __br_fdb_get(dev->br_port->br, addr); fdb = __br_fdb_get(br_port_get_rcu(dev)->br, addr);
ret = fdb && fdb->dst->dev != dev && ret = fdb && fdb->dst->dev != dev &&
fdb->dst->state == BR_STATE_FORWARDING; fdb->dst->state == BR_STATE_FORWARDING;
rcu_read_unlock(); rcu_read_unlock();
......
...@@ -147,8 +147,9 @@ static void del_nbp(struct net_bridge_port *p) ...@@ -147,8 +147,9 @@ static void del_nbp(struct net_bridge_port *p)
list_del_rcu(&p->list); list_del_rcu(&p->list);
dev->priv_flags &= ~IFF_BRIDGE_PORT;
netdev_rx_handler_unregister(dev); netdev_rx_handler_unregister(dev);
rcu_assign_pointer(dev->br_port, NULL);
br_multicast_del_port(p); br_multicast_del_port(p);
...@@ -400,7 +401,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -400,7 +401,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
return -ELOOP; return -ELOOP;
/* Device is already being bridged */ /* Device is already being bridged */
if (dev->br_port != NULL) if (br_port_exists(dev))
return -EBUSY; return -EBUSY;
/* No bridging devices that dislike that (e.g. wireless) */ /* No bridging devices that dislike that (e.g. wireless) */
...@@ -431,11 +432,11 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -431,11 +432,11 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
if (br_netpoll_info(br) && ((err = br_netpoll_enable(p)))) if (br_netpoll_info(br) && ((err = br_netpoll_enable(p))))
goto err3; goto err3;
rcu_assign_pointer(dev->br_port, p); err = netdev_rx_handler_register(dev, br_handle_frame, p);
err = netdev_rx_handler_register(dev, br_handle_frame, NULL);
if (err) if (err)
goto err4; goto err3;
dev->priv_flags |= IFF_BRIDGE_PORT;
dev_disable_lro(dev); dev_disable_lro(dev);
...@@ -457,8 +458,6 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -457,8 +458,6 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
kobject_uevent(&p->kobj, KOBJ_ADD); kobject_uevent(&p->kobj, KOBJ_ADD);
return 0; return 0;
err4:
rcu_assign_pointer(dev->br_port, NULL);
err3: err3:
sysfs_remove_link(br->ifobj, p->dev->name); sysfs_remove_link(br->ifobj, p->dev->name);
err2: err2:
...@@ -477,9 +476,13 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -477,9 +476,13 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
/* called with RTNL */ /* called with RTNL */
int br_del_if(struct net_bridge *br, struct net_device *dev) int br_del_if(struct net_bridge *br, struct net_device *dev)
{ {
struct net_bridge_port *p = dev->br_port; struct net_bridge_port *p;
if (!br_port_exists(dev))
return -EINVAL;
if (!p || p->br != br) p = br_port_get(dev);
if (p->br != br)
return -EINVAL; return -EINVAL;
del_nbp(p); del_nbp(p);
......
...@@ -41,7 +41,7 @@ static int br_pass_frame_up(struct sk_buff *skb) ...@@ -41,7 +41,7 @@ static int br_pass_frame_up(struct sk_buff *skb)
int br_handle_frame_finish(struct sk_buff *skb) int br_handle_frame_finish(struct sk_buff *skb)
{ {
const unsigned char *dest = eth_hdr(skb)->h_dest; const unsigned char *dest = eth_hdr(skb)->h_dest;
struct net_bridge_port *p = rcu_dereference(skb->dev->br_port); struct net_bridge_port *p = br_port_get_rcu(skb->dev);
struct net_bridge *br; struct net_bridge *br;
struct net_bridge_fdb_entry *dst; struct net_bridge_fdb_entry *dst;
struct net_bridge_mdb_entry *mdst; struct net_bridge_mdb_entry *mdst;
...@@ -111,10 +111,9 @@ int br_handle_frame_finish(struct sk_buff *skb) ...@@ -111,10 +111,9 @@ int br_handle_frame_finish(struct sk_buff *skb)
/* note: already called with rcu_read_lock (preempt_disabled) */ /* note: already called with rcu_read_lock (preempt_disabled) */
static int br_handle_local_finish(struct sk_buff *skb) static int br_handle_local_finish(struct sk_buff *skb)
{ {
struct net_bridge_port *p = rcu_dereference(skb->dev->br_port); struct net_bridge_port *p = br_port_get_rcu(skb->dev);
if (p) br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
return 0; /* process further */ return 0; /* process further */
} }
...@@ -151,7 +150,7 @@ struct sk_buff *br_handle_frame(struct sk_buff *skb) ...@@ -151,7 +150,7 @@ struct sk_buff *br_handle_frame(struct sk_buff *skb)
if (!skb) if (!skb)
return NULL; return NULL;
p = rcu_dereference(skb->dev->br_port); p = br_port_get_rcu(skb->dev);
if (unlikely(is_link_local(dest))) { if (unlikely(is_link_local(dest))) {
/* Pause frames shouldn't be passed up by driver anyway */ /* Pause frames shouldn't be passed up by driver anyway */
......
...@@ -127,16 +127,17 @@ void br_netfilter_rtable_init(struct net_bridge *br) ...@@ -127,16 +127,17 @@ void br_netfilter_rtable_init(struct net_bridge *br)
static inline struct rtable *bridge_parent_rtable(const struct net_device *dev) static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
{ {
struct net_bridge_port *port = rcu_dereference(dev->br_port); if (!br_port_exists(dev))
return NULL;
return port ? &port->br->fake_rtable : NULL; return &br_port_get_rcu(dev)->br->fake_rtable;
} }
static inline struct net_device *bridge_parent(const struct net_device *dev) static inline struct net_device *bridge_parent(const struct net_device *dev)
{ {
struct net_bridge_port *port = rcu_dereference(dev->br_port); if (!br_port_exists(dev))
return NULL;
return port ? port->br->dev : NULL; return br_port_get_rcu(dev)->br->dev;
} }
static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb) static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
......
...@@ -120,10 +120,11 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -120,10 +120,11 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
idx = 0; idx = 0;
for_each_netdev(net, dev) { for_each_netdev(net, dev) {
/* not a bridge port */ /* not a bridge port */
if (dev->br_port == NULL || idx < cb->args[0]) if (!br_port_exists(dev) || idx < cb->args[0])
goto skip; goto skip;
if (br_fill_ifinfo(skb, dev->br_port, NETLINK_CB(cb->skb).pid, if (br_fill_ifinfo(skb, br_port_get(dev),
NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, RTM_NEWLINK, cb->nlh->nlmsg_seq, RTM_NEWLINK,
NLM_F_MULTI) < 0) NLM_F_MULTI) < 0)
break; break;
...@@ -168,9 +169,9 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ...@@ -168,9 +169,9 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
if (!dev) if (!dev)
return -ENODEV; return -ENODEV;
p = dev->br_port; if (!br_port_exists(dev))
if (!p)
return -EINVAL; return -EINVAL;
p = br_port_get(dev);
/* if kernel STP is running, don't allow changes */ /* if kernel STP is running, don't allow changes */
if (p->br->stp_enabled == BR_KERNEL_STP) if (p->br->stp_enabled == BR_KERNEL_STP)
......
...@@ -32,14 +32,15 @@ struct notifier_block br_device_notifier = { ...@@ -32,14 +32,15 @@ struct notifier_block br_device_notifier = {
static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
{ {
struct net_device *dev = ptr; struct net_device *dev = ptr;
struct net_bridge_port *p = dev->br_port; struct net_bridge_port *p = br_port_get(dev);
struct net_bridge *br; struct net_bridge *br;
int err; int err;
/* not a port of a bridge */ /* not a port of a bridge */
if (p == NULL) if (!br_port_exists(dev))
return NOTIFY_DONE; return NOTIFY_DONE;
p = br_port_get(dev);
br = p->br; br = p->br;
switch (event) { switch (event) {
......
...@@ -150,6 +150,11 @@ struct net_bridge_port ...@@ -150,6 +150,11 @@ struct net_bridge_port
#endif #endif
}; };
#define br_port_get_rcu(dev) \
((struct net_bridge_port *) rcu_dereference(dev->rx_handler_data))
#define br_port_get(dev) ((struct net_bridge_port *) dev->rx_handler_data)
#define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
struct br_cpu_netstats { struct br_cpu_netstats {
unsigned long rx_packets; unsigned long rx_packets;
unsigned long rx_bytes; unsigned long rx_bytes;
......
...@@ -137,12 +137,13 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb, ...@@ -137,12 +137,13 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,
struct net_device *dev) struct net_device *dev)
{ {
const unsigned char *dest = eth_hdr(skb)->h_dest; const unsigned char *dest = eth_hdr(skb)->h_dest;
struct net_bridge_port *p = rcu_dereference(dev->br_port); struct net_bridge_port *p;
struct net_bridge *br; struct net_bridge *br;
const unsigned char *buf; const unsigned char *buf;
if (!p) if (!br_port_exists(dev))
goto err; goto err;
p = br_port_get_rcu(dev);
if (!pskb_may_pull(skb, 4)) if (!pskb_may_pull(skb, 4))
goto err; goto err;
......
...@@ -24,8 +24,9 @@ ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par) ...@@ -24,8 +24,9 @@ ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par)
return EBT_DROP; return EBT_DROP;
if (par->hooknum != NF_BR_BROUTING) if (par->hooknum != NF_BR_BROUTING)
/* rcu_read_lock()ed by nf_hook_slow */
memcpy(eth_hdr(skb)->h_dest, memcpy(eth_hdr(skb)->h_dest,
par->in->br_port->br->dev->dev_addr, ETH_ALEN); br_port_get_rcu(par->in)->br->dev->dev_addr, ETH_ALEN);
else else
memcpy(eth_hdr(skb)->h_dest, par->in->dev_addr, ETH_ALEN); memcpy(eth_hdr(skb)->h_dest, par->in->dev_addr, ETH_ALEN);
skb->pkt_type = PACKET_HOST; skb->pkt_type = PACKET_HOST;
......
...@@ -177,8 +177,9 @@ static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb, ...@@ -177,8 +177,9 @@ static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb,
if (in) { if (in) {
strcpy(pm->physindev, in->name); strcpy(pm->physindev, in->name);
/* If in isn't a bridge, then physindev==indev */ /* If in isn't a bridge, then physindev==indev */
if (in->br_port) if (br_port_exists(in))
strcpy(pm->indev, in->br_port->br->dev->name); /* rcu_read_lock()ed by nf_hook_slow */
strcpy(pm->indev, br_port_get_rcu(in)->br->dev->name);
else else
strcpy(pm->indev, in->name); strcpy(pm->indev, in->name);
} else } else
...@@ -187,7 +188,8 @@ static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb, ...@@ -187,7 +188,8 @@ static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb,
if (out) { if (out) {
/* If out exists, then out is a bridge port */ /* If out exists, then out is a bridge port */
strcpy(pm->physoutdev, out->name); strcpy(pm->physoutdev, out->name);
strcpy(pm->outdev, out->br_port->br->dev->name); /* rcu_read_lock()ed by nf_hook_slow */
strcpy(pm->outdev, br_port_get_rcu(out)->br->dev->name);
} else } else
pm->outdev[0] = pm->physoutdev[0] = '\0'; pm->outdev[0] = pm->physoutdev[0] = '\0';
......
...@@ -140,11 +140,14 @@ ebt_basic_match(const struct ebt_entry *e, const struct ethhdr *h, ...@@ -140,11 +140,14 @@ ebt_basic_match(const struct ebt_entry *e, const struct ethhdr *h,
return 1; return 1;
if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT)) if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
return 1; return 1;
if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check( /* rcu_read_lock()ed by nf_hook_slow */
e->logical_in, in->br_port->br->dev), EBT_ILOGICALIN)) if (in && br_port_exists(in) &&
FWINV2(ebt_dev_check(e->logical_in, br_port_get_rcu(in)->br->dev),
EBT_ILOGICALIN))
return 1; return 1;
if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check( if (out && br_port_exists(out) &&
e->logical_out, out->br_port->br->dev), EBT_ILOGICALOUT)) FWINV2(ebt_dev_check(e->logical_out, br_port_get_rcu(out)->br->dev),
EBT_ILOGICALOUT))
return 1; return 1;
if (e->bitmask & EBT_SOURCEMAC) { if (e->bitmask & EBT_SOURCEMAC) {
......
...@@ -2765,7 +2765,8 @@ int __skb_bond_should_drop(struct sk_buff *skb, struct net_device *master) ...@@ -2765,7 +2765,8 @@ int __skb_bond_should_drop(struct sk_buff *skb, struct net_device *master)
if (master->priv_flags & IFF_MASTER_ARPMON) if (master->priv_flags & IFF_MASTER_ARPMON)
dev->last_rx = jiffies; dev->last_rx = jiffies;
if ((master->priv_flags & IFF_MASTER_ALB) && master->br_port) { if ((master->priv_flags & IFF_MASTER_ALB) &&
(master->priv_flags & IFF_BRIDGE_PORT)) {
/* Do address unmangle. The local destination address /* Do address unmangle. The local destination address
* will be always the one master has. Provides the right * will be always the one master has. Provides the right
* functionality in a bridge. * functionality in a bridge.
......
...@@ -403,8 +403,9 @@ __build_packet_message(struct nfulnl_instance *inst, ...@@ -403,8 +403,9 @@ __build_packet_message(struct nfulnl_instance *inst,
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_PHYSINDEV, NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_PHYSINDEV,
htonl(indev->ifindex)); htonl(indev->ifindex));
/* this is the bridge group "brX" */ /* this is the bridge group "brX" */
/* rcu_read_lock()ed by nf_hook_slow or nf_log_packet */
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_INDEV, NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_INDEV,
htonl(indev->br_port->br->dev->ifindex)); htonl(br_port_get_rcu(indev)->br->dev->ifindex));
} else { } else {
/* Case 2: indev is bridge group, we need to look for /* Case 2: indev is bridge group, we need to look for
* physical device (when called from ipv4) */ * physical device (when called from ipv4) */
...@@ -430,8 +431,9 @@ __build_packet_message(struct nfulnl_instance *inst, ...@@ -430,8 +431,9 @@ __build_packet_message(struct nfulnl_instance *inst,
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_PHYSOUTDEV, NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_PHYSOUTDEV,
htonl(outdev->ifindex)); htonl(outdev->ifindex));
/* this is the bridge group "brX" */ /* this is the bridge group "brX" */
/* rcu_read_lock()ed by nf_hook_slow or nf_log_packet */
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_OUTDEV, NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_OUTDEV,
htonl(outdev->br_port->br->dev->ifindex)); htonl(br_port_get_rcu(outdev)->br->dev->ifindex));
} else { } else {
/* Case 2: indev is a bridge group, we need to look /* Case 2: indev is a bridge group, we need to look
* for physical device (when called from ipv4) */ * for physical device (when called from ipv4) */
......
...@@ -296,8 +296,9 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, ...@@ -296,8 +296,9 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
NLA_PUT_BE32(skb, NFQA_IFINDEX_PHYSINDEV, NLA_PUT_BE32(skb, NFQA_IFINDEX_PHYSINDEV,
htonl(indev->ifindex)); htonl(indev->ifindex));
/* this is the bridge group "brX" */ /* this is the bridge group "brX" */
/* rcu_read_lock()ed by __nf_queue */
NLA_PUT_BE32(skb, NFQA_IFINDEX_INDEV, NLA_PUT_BE32(skb, NFQA_IFINDEX_INDEV,
htonl(indev->br_port->br->dev->ifindex)); htonl(br_port_get_rcu(indev)->br->dev->ifindex));
} else { } else {
/* Case 2: indev is bridge group, we need to look for /* Case 2: indev is bridge group, we need to look for
* physical device (when called from ipv4) */ * physical device (when called from ipv4) */
...@@ -321,8 +322,9 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, ...@@ -321,8 +322,9 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
NLA_PUT_BE32(skb, NFQA_IFINDEX_PHYSOUTDEV, NLA_PUT_BE32(skb, NFQA_IFINDEX_PHYSOUTDEV,
htonl(outdev->ifindex)); htonl(outdev->ifindex));
/* this is the bridge group "brX" */ /* this is the bridge group "brX" */
/* rcu_read_lock()ed by __nf_queue */
NLA_PUT_BE32(skb, NFQA_IFINDEX_OUTDEV, NLA_PUT_BE32(skb, NFQA_IFINDEX_OUTDEV,
htonl(outdev->br_port->br->dev->ifindex)); htonl(br_port_get_rcu(outdev)->br->dev->ifindex));
} else { } else {
/* Case 2: outdev is bridge group, we need to look for /* Case 2: outdev is bridge group, we need to look for
* physical output device (when called from ipv4) */ * physical output device (when called from ipv4) */
......
...@@ -1107,7 +1107,7 @@ static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev, ...@@ -1107,7 +1107,7 @@ static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
enum nl80211_iftype iftype) enum nl80211_iftype iftype)
{ {
if (!use_4addr) { if (!use_4addr) {
if (netdev && netdev->br_port) if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
return -EBUSY; return -EBUSY;
return 0; return 0;
} }
......
...@@ -770,8 +770,8 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, ...@@ -770,8 +770,8 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* if it's part of a bridge, reject changing type to station/ibss */ /* if it's part of a bridge, reject changing type to station/ibss */
if (dev->br_port && (ntype == NL80211_IFTYPE_ADHOC || if ((dev->priv_flags & IFF_BRIDGE_PORT) &&
ntype == NL80211_IFTYPE_STATION)) (ntype == NL80211_IFTYPE_ADHOC || ntype == NL80211_IFTYPE_STATION))
return -EBUSY; return -EBUSY;
if (ntype != otype) { if (ntype != otype) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册