Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
3d702922
Commit
3d702922
authored
May 21, 2004
by
Stephen Hemminger
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[BRIDGE]: Add sysfs support.
parent
f1d65789
Changes
8
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
740 additions
and
6 deletions
+740
-6
include/linux/if_bridge.h
include/linux/if_bridge.h
+6
-0
net/bridge/Makefile
net/bridge/Makefile
+2
-0
net/bridge/br.c
net/bridge/br.c
+3
-0
net/bridge/br_if.c
net/bridge/br_if.c
+42
-6
net/bridge/br_private.h
net/bridge/br_private.h
+27
-0
net/bridge/br_stp_if.c
net/bridge/br_stp_if.c
+8
-0
net/bridge/br_sysfs_br.c
net/bridge/br_sysfs_br.c
+383
-0
net/bridge/br_sysfs_if.c
net/bridge/br_sysfs_if.c
+269
-0
No files found.
include/linux/if_bridge.h
View file @
3d702922
...
@@ -17,6 +17,12 @@
...
@@ -17,6 +17,12 @@
#include <linux/types.h>
#include <linux/types.h>
#define SYSFS_BRIDGE_ATTR "bridge"
#define SYSFS_BRIDGE_FDB "brforward"
#define SYSFS_BRIDGE_PORT_SUBDIR "brif"
#define SYSFS_BRIDGE_PORT_ATTR "brport"
#define SYSFS_BRIDGE_PORT_LINK "bridge"
#define BRCTL_VERSION 1
#define BRCTL_VERSION 1
#define BRCTL_GET_VERSION 0
#define BRCTL_GET_VERSION 0
...
...
net/bridge/Makefile
View file @
3d702922
...
@@ -8,6 +8,8 @@ bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
...
@@ -8,6 +8,8 @@ bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o
\
br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o
\
br_stp_if.o br_stp_timer.o
br_stp_if.o br_stp_timer.o
bridge-$(CONFIG_SYSFS)
+=
br_sysfs_if.o br_sysfs_br.o
bridge-$(CONFIG_BRIDGE_NETFILTER)
+=
br_netfilter.o
bridge-$(CONFIG_BRIDGE_NETFILTER)
+=
br_netfilter.o
obj-$(CONFIG_BRIDGE_NF_EBTABLES)
+=
netfilter/
obj-$(CONFIG_BRIDGE_NF_EBTABLES)
+=
netfilter/
net/bridge/br.c
View file @
3d702922
...
@@ -33,6 +33,8 @@ static int __init br_init(void)
...
@@ -33,6 +33,8 @@ static int __init br_init(void)
{
{
br_fdb_init
();
br_fdb_init
();
br_sysfs_init
();
#ifdef CONFIG_BRIDGE_NETFILTER
#ifdef CONFIG_BRIDGE_NETFILTER
if
(
br_netfilter_init
())
if
(
br_netfilter_init
())
return
1
;
return
1
;
...
@@ -67,6 +69,7 @@ static void __exit br_deinit(void)
...
@@ -67,6 +69,7 @@ static void __exit br_deinit(void)
#endif
#endif
br_handle_frame_hook
=
NULL
;
br_handle_frame_hook
=
NULL
;
br_sysfs_fini
();
br_fdb_fini
();
br_fdb_fini
();
}
}
...
...
net/bridge/br_if.c
View file @
3d702922
...
@@ -81,8 +81,11 @@ static void destroy_nbp(void *arg)
...
@@ -81,8 +81,11 @@ static void destroy_nbp(void *arg)
struct
net_device
*
dev
=
p
->
dev
;
struct
net_device
*
dev
=
p
->
dev
;
dev
->
br_port
=
NULL
;
dev
->
br_port
=
NULL
;
p
->
br
=
NULL
;
p
->
dev
=
NULL
;
dev_put
(
dev
);
dev_put
(
dev
);
kfree
(
p
);
br_sysfs_freeif
(
p
);
}
}
/* called with RTNL */
/* called with RTNL */
...
@@ -111,14 +114,16 @@ static void del_nbp(struct net_bridge_port *p)
...
@@ -111,14 +114,16 @@ static void del_nbp(struct net_bridge_port *p)
/* called with RTNL */
/* called with RTNL */
static
void
del_br
(
struct
net_bridge
*
br
)
static
void
del_br
(
struct
net_bridge
*
br
)
{
{
struct
list_head
*
p
,
*
n
;
struct
net_bridge_port
*
p
,
*
n
;
list_for_each_safe
(
p
,
n
,
&
br
->
port_list
)
{
list_for_each_entry_safe
(
p
,
n
,
&
br
->
port_list
,
list
)
{
del_nbp
(
list_entry
(
p
,
struct
net_bridge_port
,
list
));
br_sysfs_removeif
(
p
);
del_nbp
(
p
);
}
}
del_timer_sync
(
&
br
->
gc_timer
);
del_timer_sync
(
&
br
->
gc_timer
);
br_sysfs_delbr
(
br
->
dev
);
unregister_netdevice
(
br
->
dev
);
unregister_netdevice
(
br
->
dev
);
}
}
...
@@ -210,6 +215,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
...
@@ -210,6 +215,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
p
->
port_no
=
index
;
p
->
port_no
=
index
;
br_init_port
(
p
);
br_init_port
(
p
);
p
->
state
=
BR_STATE_DISABLED
;
p
->
state
=
BR_STATE_DISABLED
;
kobject_init
(
&
p
->
kobj
);
return
p
;
return
p
;
}
}
...
@@ -223,10 +229,37 @@ int br_add_bridge(const char *name)
...
@@ -223,10 +229,37 @@ int br_add_bridge(const char *name)
if
(
!
dev
)
if
(
!
dev
)
return
-
ENOMEM
;
return
-
ENOMEM
;
ret
=
register_netdev
(
dev
);
rtnl_lock
();
if
(
strchr
(
dev
->
name
,
'%'
))
{
ret
=
dev_alloc_name
(
dev
,
dev
->
name
);
if
(
ret
<
0
)
goto
err1
;
}
ret
=
register_netdevice
(
dev
);
if
(
ret
)
if
(
ret
)
free_netdev
(
dev
);
goto
err2
;
/* network device kobject is not setup until
* after rtnl_unlock does it's hotplug magic.
* so hold reference to avoid race.
*/
dev_hold
(
dev
);
rtnl_unlock
();
ret
=
br_sysfs_addbr
(
dev
);
dev_put
(
dev
);
if
(
ret
)
unregister_netdev
(
dev
);
out:
return
ret
;
return
ret
;
err2:
free_netdev
(
dev
);
err1:
rtnl_unlock
();
goto
out
;
}
}
int
br_del_bridge
(
const
char
*
name
)
int
br_del_bridge
(
const
char
*
name
)
...
@@ -277,6 +310,8 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
...
@@ -277,6 +310,8 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
if
((
err
=
br_fdb_insert
(
br
,
p
,
dev
->
dev_addr
,
1
)))
if
((
err
=
br_fdb_insert
(
br
,
p
,
dev
->
dev_addr
,
1
)))
destroy_nbp
(
p
);
destroy_nbp
(
p
);
else
if
((
err
=
br_sysfs_addif
(
p
)))
del_nbp
(
p
);
else
{
else
{
dev_set_promiscuity
(
dev
,
1
);
dev_set_promiscuity
(
dev
,
1
);
...
@@ -300,6 +335,7 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
...
@@ -300,6 +335,7 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
if
(
!
p
||
p
->
br
!=
br
)
if
(
!
p
||
p
->
br
!=
br
)
return
-
EINVAL
;
return
-
EINVAL
;
br_sysfs_removeif
(
p
);
del_nbp
(
p
);
del_nbp
(
p
);
spin_lock_bh
(
&
br
->
lock
);
spin_lock_bh
(
&
br
->
lock
);
...
...
net/bridge/br_private.h
View file @
3d702922
...
@@ -76,6 +76,7 @@ struct net_bridge_port
...
@@ -76,6 +76,7 @@ struct net_bridge_port
struct
timer_list
forward_delay_timer
;
struct
timer_list
forward_delay_timer
;
struct
timer_list
hold_timer
;
struct
timer_list
hold_timer
;
struct
timer_list
message_age_timer
;
struct
timer_list
message_age_timer
;
struct
kobject
kobj
;
struct
rcu_head
rcu
;
struct
rcu_head
rcu
;
};
};
...
@@ -110,6 +111,7 @@ struct net_bridge
...
@@ -110,6 +111,7 @@ struct net_bridge
struct
timer_list
tcn_timer
;
struct
timer_list
tcn_timer
;
struct
timer_list
topology_change_timer
;
struct
timer_list
topology_change_timer
;
struct
timer_list
gc_timer
;
struct
timer_list
gc_timer
;
struct
kobject
ifobj
;
};
};
extern
struct
notifier_block
br_device_notifier
;
extern
struct
notifier_block
br_device_notifier
;
...
@@ -198,6 +200,7 @@ extern void br_stp_set_port_priority(struct net_bridge_port *p,
...
@@ -198,6 +200,7 @@ extern void br_stp_set_port_priority(struct net_bridge_port *p,
u8
newprio
);
u8
newprio
);
extern
void
br_stp_set_path_cost
(
struct
net_bridge_port
*
p
,
extern
void
br_stp_set_path_cost
(
struct
net_bridge_port
*
p
,
u32
path_cost
);
u32
path_cost
);
extern
ssize_t
br_show_bridge_id
(
char
*
buf
,
const
struct
bridge_id
*
id
);
/* br_stp_bpdu.c */
/* br_stp_bpdu.c */
extern
int
br_stp_handle_bpdu
(
struct
sk_buff
*
skb
);
extern
int
br_stp_handle_bpdu
(
struct
sk_buff
*
skb
);
...
@@ -207,4 +210,28 @@ extern void br_stp_timer_init(struct net_bridge *br);
...
@@ -207,4 +210,28 @@ extern void br_stp_timer_init(struct net_bridge *br);
extern
void
br_stp_port_timer_init
(
struct
net_bridge_port
*
p
);
extern
void
br_stp_port_timer_init
(
struct
net_bridge_port
*
p
);
extern
unsigned
long
br_timer_value
(
const
struct
timer_list
*
timer
);
extern
unsigned
long
br_timer_value
(
const
struct
timer_list
*
timer
);
#ifdef CONFIG_SYSFS
/* br_sysfs_if.c */
extern
int
br_sysfs_addif
(
struct
net_bridge_port
*
p
);
extern
void
br_sysfs_removeif
(
struct
net_bridge_port
*
p
);
extern
void
br_sysfs_freeif
(
struct
net_bridge_port
*
p
);
/* br_sysfs_br.c */
extern
struct
subsystem
bridge_subsys
;
extern
void
br_sysfs_init
(
void
);
extern
void
br_sysfs_fini
(
void
);
extern
int
br_sysfs_addbr
(
struct
net_device
*
dev
);
extern
void
br_sysfs_delbr
(
struct
net_device
*
dev
);
#else
#define br_sysfs_addif(p) (0)
#define br_sysfs_removeif(p) do { } while(0)
#define br_sysfs_freeif(p) kfree(p)
#define br_sysfs_init() do { } while(0)
#define br_sysfs_fini() do { } while(0)
#define br_sysfs_addbr(dev) (0)
#define br_sysfs_delbr(dev) do { } while(0)
#endif
/* CONFIG_SYSFS */
#endif
#endif
net/bridge/br_stp_if.c
View file @
3d702922
...
@@ -212,3 +212,11 @@ void br_stp_set_path_cost(struct net_bridge_port *p, u32 path_cost)
...
@@ -212,3 +212,11 @@ void br_stp_set_path_cost(struct net_bridge_port *p, u32 path_cost)
br_configuration_update
(
p
->
br
);
br_configuration_update
(
p
->
br
);
br_port_state_selection
(
p
->
br
);
br_port_state_selection
(
p
->
br
);
}
}
ssize_t
br_show_bridge_id
(
char
*
buf
,
const
struct
bridge_id
*
id
)
{
return
sprintf
(
buf
,
"%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x
\n
"
,
id
->
prio
[
0
],
id
->
prio
[
1
],
id
->
addr
[
0
],
id
->
addr
[
1
],
id
->
addr
[
2
],
id
->
addr
[
3
],
id
->
addr
[
4
],
id
->
addr
[
5
]);
}
net/bridge/br_sysfs_br.c
0 → 100644
View file @
3d702922
This diff is collapsed.
Click to expand it.
net/bridge/br_sysfs_if.c
0 → 100644
View file @
3d702922
/*
* Sysfs attributes of bridge ports
* Linux ethernet bridge
*
* Authors:
* Stephen Hemminger <shemminger@osdl.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/if_bridge.h>
#include <linux/rtnetlink.h>
#include <linux/spinlock.h>
#include "br_private.h"
struct
brport_attribute
{
struct
attribute
attr
;
ssize_t
(
*
show
)(
struct
net_bridge_port
*
,
char
*
);
ssize_t
(
*
store
)(
struct
net_bridge_port
*
,
unsigned
long
);
};
#define BRPORT_ATTR(_name,_mode,_show,_store) \
struct brport_attribute brport_attr_##_name = { \
.attr = {.name = __stringify(_name), \
.mode = _mode, \
.owner = THIS_MODULE, }, \
.show = _show, \
.store = _store, \
};
static
ssize_t
show_path_cost
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
sprintf
(
buf
,
"%d
\n
"
,
p
->
path_cost
);
}
static
ssize_t
store_path_cost
(
struct
net_bridge_port
*
p
,
unsigned
long
v
)
{
br_stp_set_path_cost
(
p
,
v
);
return
0
;
}
static
BRPORT_ATTR
(
path_cost
,
S_IRUGO
|
S_IWUSR
,
show_path_cost
,
store_path_cost
);
static
ssize_t
show_priority
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
sprintf
(
buf
,
"%d
\n
"
,
p
->
priority
);
}
static
ssize_t
store_priority
(
struct
net_bridge_port
*
p
,
unsigned
long
v
)
{
if
(
v
>=
(
1
<<
(
16
-
BR_PORT_BITS
)))
return
-
ERANGE
;
br_stp_set_port_priority
(
p
,
v
);
return
0
;
}
static
BRPORT_ATTR
(
priority
,
S_IRUGO
|
S_IWUSR
,
show_priority
,
store_priority
);
static
ssize_t
show_designated_root
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
br_show_bridge_id
(
buf
,
&
p
->
designated_root
);
}
static
BRPORT_ATTR
(
designated_root
,
S_IRUGO
,
show_designated_root
,
NULL
);
static
ssize_t
show_designated_bridge
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
br_show_bridge_id
(
buf
,
&
p
->
designated_bridge
);
}
static
BRPORT_ATTR
(
designated_bridge
,
S_IRUGO
,
show_designated_bridge
,
NULL
);
static
ssize_t
show_designated_port
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
sprintf
(
buf
,
"%d
\n
"
,
p
->
designated_port
);
}
static
BRPORT_ATTR
(
designated_port
,
S_IRUGO
,
show_designated_port
,
NULL
);
static
ssize_t
show_designated_cost
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
sprintf
(
buf
,
"%d
\n
"
,
p
->
designated_cost
);
}
static
BRPORT_ATTR
(
designated_cost
,
S_IRUGO
,
show_designated_cost
,
NULL
);
static
ssize_t
show_port_id
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
sprintf
(
buf
,
"0x%x
\n
"
,
p
->
port_id
);
}
static
BRPORT_ATTR
(
port_id
,
S_IRUGO
,
show_port_id
,
NULL
);
static
ssize_t
show_port_no
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
sprintf
(
buf
,
"0x%x
\n
"
,
p
->
port_no
);
}
static
BRPORT_ATTR
(
port_no
,
S_IRUGO
,
show_port_no
,
NULL
);
static
ssize_t
show_change_ack
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
sprintf
(
buf
,
"%d
\n
"
,
p
->
topology_change_ack
);
}
static
BRPORT_ATTR
(
change_ack
,
S_IRUGO
,
show_change_ack
,
NULL
);
static
ssize_t
show_config_pending
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
sprintf
(
buf
,
"%d
\n
"
,
p
->
config_pending
);
}
static
BRPORT_ATTR
(
config_pending
,
S_IRUGO
,
show_config_pending
,
NULL
);
static
ssize_t
show_port_state
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
sprintf
(
buf
,
"%d
\n
"
,
p
->
state
);
}
static
BRPORT_ATTR
(
state
,
S_IRUGO
,
show_port_state
,
NULL
);
static
ssize_t
show_message_age_timer
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
sprintf
(
buf
,
"%ld
\n
"
,
br_timer_value
(
&
p
->
message_age_timer
));
}
static
BRPORT_ATTR
(
message_age_timer
,
S_IRUGO
,
show_message_age_timer
,
NULL
);
static
ssize_t
show_forward_delay_timer
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
sprintf
(
buf
,
"%ld
\n
"
,
br_timer_value
(
&
p
->
forward_delay_timer
));
}
static
BRPORT_ATTR
(
forward_delay_timer
,
S_IRUGO
,
show_forward_delay_timer
,
NULL
);
static
ssize_t
show_hold_timer
(
struct
net_bridge_port
*
p
,
char
*
buf
)
{
return
sprintf
(
buf
,
"%ld
\n
"
,
br_timer_value
(
&
p
->
hold_timer
));
}
static
BRPORT_ATTR
(
hold_timer
,
S_IRUGO
,
show_hold_timer
,
NULL
);
static
struct
brport_attribute
*
brport_attrs
[]
=
{
&
brport_attr_path_cost
,
&
brport_attr_priority
,
&
brport_attr_port_id
,
&
brport_attr_port_no
,
&
brport_attr_designated_root
,
&
brport_attr_designated_bridge
,
&
brport_attr_designated_port
,
&
brport_attr_designated_cost
,
&
brport_attr_state
,
&
brport_attr_change_ack
,
&
brport_attr_config_pending
,
&
brport_attr_message_age_timer
,
&
brport_attr_forward_delay_timer
,
&
brport_attr_hold_timer
,
NULL
};
#define to_brport_attr(_at) container_of(_at, struct brport_attribute, attr)
#define to_brport(obj) container_of(obj, struct net_bridge_port, kobj)
static
ssize_t
brport_show
(
struct
kobject
*
kobj
,
struct
attribute
*
attr
,
char
*
buf
)
{
struct
brport_attribute
*
brport_attr
=
to_brport_attr
(
attr
);
struct
net_bridge_port
*
p
=
to_brport
(
kobj
);
return
brport_attr
->
show
(
p
,
buf
);
}
static
ssize_t
brport_store
(
struct
kobject
*
kobj
,
struct
attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
struct
brport_attribute
*
brport_attr
=
to_brport_attr
(
attr
);
struct
net_bridge_port
*
p
=
to_brport
(
kobj
);
ssize_t
ret
=
-
EINVAL
;
char
*
endp
;
unsigned
long
val
;
if
(
!
capable
(
CAP_NET_ADMIN
))
return
-
EPERM
;
val
=
simple_strtoul
(
buf
,
&
endp
,
0
);
if
(
endp
!=
buf
)
{
rtnl_lock
();
if
(
p
->
dev
&&
p
->
br
&&
brport_attr
->
store
)
{
spin_lock_bh
(
&
p
->
br
->
lock
);
ret
=
brport_attr
->
store
(
p
,
val
);
spin_unlock_bh
(
&
p
->
br
->
lock
);
if
(
ret
==
0
)
ret
=
count
;
}
rtnl_unlock
();
}
return
ret
;
}
/* called from kobject_put when port ref count goes to zero. */
static
void
brport_release
(
struct
kobject
*
kobj
)
{
kfree
(
container_of
(
kobj
,
struct
net_bridge_port
,
kobj
));
}
static
struct
sysfs_ops
brport_sysfs_ops
=
{
.
show
=
brport_show
,
.
store
=
brport_store
,
};
static
struct
kobj_type
brport_ktype
=
{
.
sysfs_ops
=
&
brport_sysfs_ops
,
.
release
=
brport_release
,
};
/*
* Add sysfs entries to ethernet device added to a bridge.
* Creates a brport subdirectory with bridge attributes.
* Puts symlink in bridge's brport subdirectory
*/
int
br_sysfs_addif
(
struct
net_bridge_port
*
p
)
{
struct
net_bridge
*
br
=
p
->
br
;
struct
brport_attribute
**
a
;
int
err
;
ASSERT_RTNL
();
kobject_set_name
(
&
p
->
kobj
,
SYSFS_BRIDGE_PORT_ATTR
);
p
->
kobj
.
ktype
=
&
brport_ktype
;
p
->
kobj
.
parent
=
&
(
p
->
dev
->
class_dev
.
kobj
);
p
->
kobj
.
kset
=
&
bridge_subsys
.
kset
;
err
=
kobject_add
(
&
p
->
kobj
);
if
(
err
)
goto
out1
;
err
=
sysfs_create_link
(
&
p
->
kobj
,
&
br
->
dev
->
class_dev
.
kobj
,
SYSFS_BRIDGE_PORT_LINK
);
if
(
err
)
goto
out2
;
for
(
a
=
brport_attrs
;
*
a
;
++
a
)
{
err
=
sysfs_create_file
(
&
p
->
kobj
,
&
((
*
a
)
->
attr
));
if
(
err
)
goto
out2
;
}
err
=
sysfs_create_link
(
&
br
->
ifobj
,
&
p
->
kobj
,
p
->
dev
->
name
);
if
(
err
)
goto
out2
;
return
0
;
out2:
kobject_del
(
&
p
->
kobj
);
out1:
return
err
;
}
void
br_sysfs_removeif
(
struct
net_bridge_port
*
p
)
{
pr_debug
(
"br_sysfs_removeif
\n
"
);
sysfs_remove_link
(
&
p
->
br
->
ifobj
,
p
->
dev
->
name
);
kobject_del
(
&
p
->
kobj
);
}
void
br_sysfs_freeif
(
struct
net_bridge_port
*
p
)
{
pr_debug
(
"br_sysfs_freeif
\n
"
);
kobject_put
(
&
p
->
kobj
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment