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
Kirill Smelkov
linux
Commits
0e43182c
Commit
0e43182c
authored
Aug 24, 2011
by
David S. Miller
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'batman-adv/next' of
git://git.open-mesh.org/linux-merge
parents
131ea667
a943cac1
Changes
21
Show whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
291 additions
and
148 deletions
+291
-148
Documentation/ABI/testing/sysfs-class-net-mesh
Documentation/ABI/testing/sysfs-class-net-mesh
+8
-0
net/batman-adv/aggregation.h
net/batman-adv/aggregation.h
+1
-2
net/batman-adv/bat_sysfs.c
net/batman-adv/bat_sysfs.c
+2
-0
net/batman-adv/bitarray.c
net/batman-adv/bitarray.c
+3
-3
net/batman-adv/gateway_client.c
net/batman-adv/gateway_client.c
+5
-5
net/batman-adv/hard-interface.c
net/batman-adv/hard-interface.c
+32
-2
net/batman-adv/hard-interface.h
net/batman-adv/hard-interface.h
+1
-0
net/batman-adv/hash.h
net/batman-adv/hash.h
+19
-6
net/batman-adv/main.c
net/batman-adv/main.c
+1
-1
net/batman-adv/main.h
net/batman-adv/main.h
+4
-2
net/batman-adv/originator.c
net/batman-adv/originator.c
+1
-1
net/batman-adv/packet.h
net/batman-adv/packet.h
+1
-0
net/batman-adv/routing.c
net/batman-adv/routing.c
+8
-69
net/batman-adv/send.c
net/batman-adv/send.c
+5
-5
net/batman-adv/soft-interface.c
net/batman-adv/soft-interface.c
+9
-4
net/batman-adv/translation-table.c
net/batman-adv/translation-table.c
+170
-29
net/batman-adv/translation-table.h
net/batman-adv/translation-table.h
+10
-11
net/batman-adv/types.h
net/batman-adv/types.h
+3
-2
net/batman-adv/unicast.c
net/batman-adv/unicast.c
+4
-2
net/batman-adv/unicast.h
net/batman-adv/unicast.h
+1
-1
net/batman-adv/vis.c
net/batman-adv/vis.c
+3
-3
No files found.
Documentation/ABI/testing/sysfs-class-net-mesh
View file @
0e43182c
...
...
@@ -22,6 +22,14 @@ Description:
mesh will be fragmented or silently discarded if the
packet size exceeds the outgoing interface MTU.
What: /sys/class/net/<mesh_iface>/mesh/ap_isolation
Date: May 2011
Contact: Antonio Quartulli <ordex@autistici.org>
Description:
Indicates whether the data traffic going from a
wireless client to another wireless client will be
silently dropped.
What: /sys/class/net/<mesh_iface>/mesh/gw_bandwidth
Date: October 2010
Contact: Marek Lindner <lindner_marek@yahoo.de>
...
...
net/batman-adv/aggregation.h
View file @
0e43182c
...
...
@@ -28,8 +28,7 @@
static
inline
int
aggregated_packet
(
int
buff_pos
,
int
packet_len
,
int
tt_num_changes
)
{
int
next_buff_pos
=
buff_pos
+
BAT_PACKET_LEN
+
(
tt_num_changes
*
sizeof
(
struct
tt_change
));
int
next_buff_pos
=
buff_pos
+
BAT_PACKET_LEN
+
tt_len
(
tt_num_changes
);
return
(
next_buff_pos
<=
packet_len
)
&&
(
next_buff_pos
<=
MAX_AGGREGATION_BYTES
);
...
...
net/batman-adv/bat_sysfs.c
View file @
0e43182c
...
...
@@ -380,6 +380,7 @@ static ssize_t store_gw_bwidth(struct kobject *kobj, struct attribute *attr,
BAT_ATTR_BOOL
(
aggregated_ogms
,
S_IRUGO
|
S_IWUSR
,
NULL
);
BAT_ATTR_BOOL
(
bonding
,
S_IRUGO
|
S_IWUSR
,
NULL
);
BAT_ATTR_BOOL
(
fragmentation
,
S_IRUGO
|
S_IWUSR
,
update_min_mtu
);
BAT_ATTR_BOOL
(
ap_isolation
,
S_IRUGO
|
S_IWUSR
,
NULL
);
static
BAT_ATTR
(
vis_mode
,
S_IRUGO
|
S_IWUSR
,
show_vis_mode
,
store_vis_mode
);
static
BAT_ATTR
(
gw_mode
,
S_IRUGO
|
S_IWUSR
,
show_gw_mode
,
store_gw_mode
);
BAT_ATTR_UINT
(
orig_interval
,
S_IRUGO
|
S_IWUSR
,
2
*
JITTER
,
INT_MAX
,
NULL
);
...
...
@@ -396,6 +397,7 @@ static struct bat_attribute *mesh_attrs[] = {
&
bat_attr_aggregated_ogms
,
&
bat_attr_bonding
,
&
bat_attr_fragmentation
,
&
bat_attr_ap_isolation
,
&
bat_attr_vis_mode
,
&
bat_attr_gw_mode
,
&
bat_attr_orig_interval
,
...
...
net/batman-adv/bitarray.c
View file @
0e43182c
...
...
@@ -97,12 +97,12 @@ static void bit_shift(unsigned long *seq_bits, int32_t n)
(
seq_bits
[
i
-
word_num
-
1
]
>>
(
WORD_BIT_SIZE
-
word_offset
));
/* and the upper part of the right half and shift it left to
* it
'
s position */
* its position */
/* for our example that would be: word[0] = 9800 + 0076 =
* 9876 */
}
/* now for our last word, i==word_num, we only have
the it's "left"
*
half.
that's the 1000 word in our example.*/
/* now for our last word, i==word_num, we only have
its "left" half.
* that's the 1000 word in our example.*/
seq_bits
[
i
]
=
(
seq_bits
[
i
-
word_num
]
<<
word_offset
);
...
...
net/batman-adv/gateway_client.c
View file @
0e43182c
...
...
@@ -532,14 +532,14 @@ static bool is_type_dhcprequest(struct sk_buff *skb, int header_len)
pkt_len
-=
header_len
+
DHCP_OPTIONS_OFFSET
+
1
;
/* Access the dhcp option lists. Each entry is made up by:
* - octe
c
t 1: option type
* - octe
c
t 2: option data len (only if type != 255 and 0)
* - octe
c
t 3: option data */
* - octet 1: option type
* - octet 2: option data len (only if type != 255 and 0)
* - octet 3: option data */
while
(
*
p
!=
255
&&
!
ret
)
{
/* p now points to the first octe
c
t: option type */
/* p now points to the first octet: option type */
if
(
*
p
==
53
)
{
/* type 53 is the message type option.
* Jump the len octe
ct and go to the data octec
t */
* Jump the len octe
t and go to the data octe
t */
if
(
pkt_len
<
2
)
goto
out
;
p
+=
2
;
...
...
net/batman-adv/hard-interface.c
View file @
0e43182c
...
...
@@ -249,7 +249,7 @@ static void hardif_activate_interface(struct hard_iface *hard_iface)
/**
* the first active interface becomes our primary interface or
* the next active interface after the old primay interface was removed
* the next active interface after the old prima
r
y interface was removed
*/
primary_if
=
primary_if_get_selected
(
bat_priv
);
if
(
!
primary_if
)
...
...
@@ -573,7 +573,7 @@ static int hard_if_event(struct notifier_block *this,
return
NOTIFY_DONE
;
}
/*
receive a packet with the batman ethertype coming on a
hard
/*
incoming packets with the batman ethertype received on any active
hard
* interface */
static
int
batman_skb_recv
(
struct
sk_buff
*
skb
,
struct
net_device
*
dev
,
struct
packet_type
*
ptype
,
...
...
@@ -681,6 +681,36 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
return
NET_RX_DROP
;
}
/* This function returns true if the interface represented by ifindex is a
* 802.11 wireless device */
bool
is_wifi_iface
(
int
ifindex
)
{
struct
net_device
*
net_device
=
NULL
;
bool
ret
=
false
;
if
(
ifindex
==
NULL_IFINDEX
)
goto
out
;
net_device
=
dev_get_by_index
(
&
init_net
,
ifindex
);
if
(
!
net_device
)
goto
out
;
#ifdef CONFIG_WIRELESS_EXT
/* pre-cfg80211 drivers have to implement WEXT, so it is possible to
* check for wireless_handlers != NULL */
if
(
net_device
->
wireless_handlers
)
ret
=
true
;
else
#endif
/* cfg80211 drivers have to set ieee80211_ptr */
if
(
net_device
->
ieee80211_ptr
)
ret
=
true
;
out:
if
(
net_device
)
dev_put
(
net_device
);
return
ret
;
}
struct
notifier_block
hard_if_notifier
=
{
.
notifier_call
=
hard_if_event
,
};
net/batman-adv/hard-interface.h
View file @
0e43182c
...
...
@@ -42,6 +42,7 @@ void hardif_remove_interfaces(void);
int
hardif_min_mtu
(
struct
net_device
*
soft_iface
);
void
update_min_mtu
(
struct
net_device
*
soft_iface
);
void
hardif_free_rcu
(
struct
rcu_head
*
rcu
);
bool
is_wifi_iface
(
int
ifindex
);
static
inline
void
hardif_free_ref
(
struct
hard_iface
*
hard_iface
)
{
...
...
net/batman-adv/hash.h
View file @
0e43182c
...
...
@@ -76,19 +76,30 @@ static inline void hash_delete(struct hashtable_t *hash,
hash_destroy
(
hash
);
}
/* adds data to the hashtable. returns 0 on success, -1 on error */
/**
* hash_add - adds data to the hashtable
* @hash: storage hash table
* @compare: callback to determine if 2 hash elements are identical
* @choose: callback calculating the hash index
* @data: data passed to the aforementioned callbacks as argument
* @data_node: to be added element
*
* Returns 0 on success, 1 if the element already is in the hash
* and -1 on error.
*/
static
inline
int
hash_add
(
struct
hashtable_t
*
hash
,
hashdata_compare_cb
compare
,
hashdata_choose_cb
choose
,
const
void
*
data
,
struct
hlist_node
*
data_node
)
{
int
index
;
int
index
,
ret
=
-
1
;
struct
hlist_head
*
head
;
struct
hlist_node
*
node
;
spinlock_t
*
list_lock
;
/* spinlock to protect write access */
if
(
!
hash
)
goto
err
;
goto
out
;
index
=
choose
(
data
,
hash
->
size
);
head
=
&
hash
->
table
[
index
];
...
...
@@ -99,6 +110,7 @@ static inline int hash_add(struct hashtable_t *hash,
if
(
!
compare
(
node
,
data
))
continue
;
ret
=
1
;
goto
err_unlock
;
}
rcu_read_unlock
();
...
...
@@ -108,12 +120,13 @@ static inline int hash_add(struct hashtable_t *hash,
hlist_add_head_rcu
(
data_node
,
head
);
spin_unlock_bh
(
list_lock
);
return
0
;
ret
=
0
;
goto
out
;
err_unlock:
rcu_read_unlock
();
err
:
return
-
1
;
out
:
return
ret
;
}
/* removes data from hash, if found. returns pointer do data on success, so you
...
...
net/batman-adv/main.c
View file @
0e43182c
...
...
@@ -107,7 +107,7 @@ int mesh_init(struct net_device *soft_iface)
if
(
tt_init
(
bat_priv
)
<
1
)
goto
err
;
tt_local_add
(
soft_iface
,
soft_iface
->
dev_addr
);
tt_local_add
(
soft_iface
,
soft_iface
->
dev_addr
,
NULL_IFINDEX
);
if
(
vis_init
(
bat_priv
)
<
1
)
goto
err
;
...
...
net/batman-adv/main.h
View file @
0e43182c
...
...
@@ -44,7 +44,7 @@
#define PURGE_TIMEOUT 200
#define TT_LOCAL_TIMEOUT 3600
/* in seconds */
#define TT_CLIENT_ROAM_TIMEOUT 600
/* sliding packet range of received originator messages in squence numbers
/* sliding packet range of received originator messages in s
e
quence numbers
* (should be a multiple of our word size) */
#define TQ_LOCAL_WINDOW_SIZE 64
#define TT_REQUEST_TIMEOUT 3
/* seconds we have to keep pending tt_req */
...
...
@@ -62,6 +62,8 @@
#define NO_FLAGS 0
#define NULL_IFINDEX 0
/* dummy ifindex used to avoid iface checks */
#define NUM_WORDS (TQ_LOCAL_WINDOW_SIZE / WORD_BIT_SIZE)
#define LOG_BUF_LEN 8192
/* has to be a power of 2 */
...
...
@@ -133,7 +135,7 @@ enum dbg_level {
#include <linux/mutex.h>
/* mutex */
#include <linux/module.h>
/* needed by all modules */
#include <linux/netdevice.h>
/* netdevice */
#include <linux/etherdevice.h>
/* ethernet address classif
ac
tion */
#include <linux/etherdevice.h>
/* ethernet address classif
ica
tion */
#include <linux/if_ether.h>
/* ethernet header */
#include <linux/poll.h>
/* poll_table */
#include <linux/kthread.h>
/* kernel threads */
...
...
net/batman-adv/originator.c
View file @
0e43182c
...
...
@@ -252,7 +252,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, const uint8_t *addr)
hash_added
=
hash_add
(
bat_priv
->
orig_hash
,
compare_orig
,
choose_orig
,
orig_node
,
&
orig_node
->
hash_entry
);
if
(
hash_added
<
0
)
if
(
hash_added
!=
0
)
goto
free_bcast_own_sum
;
return
orig_node
;
...
...
net/batman-adv/packet.h
View file @
0e43182c
...
...
@@ -84,6 +84,7 @@ enum tt_query_flags {
enum
tt_client_flags
{
TT_CLIENT_DEL
=
1
<<
0
,
TT_CLIENT_ROAM
=
1
<<
1
,
TT_CLIENT_WIFI
=
1
<<
2
,
TT_CLIENT_NOPURGE
=
1
<<
8
,
TT_CLIENT_NEW
=
1
<<
9
,
TT_CLIENT_PENDING
=
1
<<
10
...
...
net/batman-adv/routing.c
View file @
0e43182c
...
...
@@ -64,66 +64,6 @@ void slide_own_bcast_window(struct hard_iface *hard_iface)
}
}
static
void
update_transtable
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
,
const
unsigned
char
*
tt_buff
,
uint8_t
tt_num_changes
,
uint8_t
ttvn
,
uint16_t
tt_crc
)
{
uint8_t
orig_ttvn
=
(
uint8_t
)
atomic_read
(
&
orig_node
->
last_ttvn
);
bool
full_table
=
true
;
/* the ttvn increased by one -> we can apply the attached changes */
if
(
ttvn
-
orig_ttvn
==
1
)
{
/* the OGM could not contain the changes because they were too
* many to fit in one frame or because they have already been
* sent TT_OGM_APPEND_MAX times. In this case send a tt
* request */
if
(
!
tt_num_changes
)
{
full_table
=
false
;
goto
request_table
;
}
tt_update_changes
(
bat_priv
,
orig_node
,
tt_num_changes
,
ttvn
,
(
struct
tt_change
*
)
tt_buff
);
/* Even if we received the crc into the OGM, we prefer
* to recompute it to spot any possible inconsistency
* in the global table */
orig_node
->
tt_crc
=
tt_global_crc
(
bat_priv
,
orig_node
);
/* The ttvn alone is not enough to guarantee consistency
* because a single value could repesent different states
* (due to the wrap around). Thus a node has to check whether
* the resulting table (after applying the changes) is still
* consistent or not. E.g. a node could disconnect while its
* ttvn is X and reconnect on ttvn = X + TTVN_MAX: in this case
* checking the CRC value is mandatory to detect the
* inconsistency */
if
(
orig_node
->
tt_crc
!=
tt_crc
)
goto
request_table
;
/* Roaming phase is over: tables are in sync again. I can
* unset the flag */
orig_node
->
tt_poss_change
=
false
;
}
else
{
/* if we missed more than one change or our tables are not
* in sync anymore -> request fresh tt data */
if
(
ttvn
!=
orig_ttvn
||
orig_node
->
tt_crc
!=
tt_crc
)
{
request_table:
bat_dbg
(
DBG_TT
,
bat_priv
,
"TT inconsistency for %pM. "
"Need to retrieve the correct information "
"(ttvn: %u last_ttvn: %u crc: %u last_crc: "
"%u num_changes: %u)
\n
"
,
orig_node
->
orig
,
ttvn
,
orig_ttvn
,
tt_crc
,
orig_node
->
tt_crc
,
tt_num_changes
);
send_tt_request
(
bat_priv
,
orig_node
,
ttvn
,
tt_crc
,
full_table
);
return
;
}
}
}
static
void
update_route
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
,
struct
neigh_node
*
neigh_node
)
...
...
@@ -228,7 +168,7 @@ static int is_bidirectional_neigh(struct orig_node *orig_node,
if
(
!
neigh_node
)
goto
out
;
/* if orig_node is direct neighbo
u
r update neigh_node last_valid */
/* if orig_node is direct neighbor update neigh_node last_valid */
if
(
orig_node
==
orig_neigh_node
)
neigh_node
->
last_valid
=
jiffies
;
...
...
@@ -473,7 +413,7 @@ static void update_orig(struct bat_priv *bat_priv, struct orig_node *orig_node,
if
(
router
&&
(
router
->
tq_avg
>
neigh_node
->
tq_avg
))
goto
update_tt
;
/* if the TQ is the same and the link not more symetric we
/* if the TQ is the same and the link not more sym
m
etric we
* won't consider it either */
if
(
router
&&
(
neigh_node
->
tq_avg
==
router
->
tq_avg
))
{
orig_node_tmp
=
router
->
orig_node
;
...
...
@@ -500,10 +440,9 @@ static void update_orig(struct bat_priv *bat_priv, struct orig_node *orig_node,
if
(((
batman_packet
->
orig
!=
ethhdr
->
h_source
)
&&
(
batman_packet
->
ttl
>
2
))
||
(
batman_packet
->
flags
&
PRIMARIES_FIRST_HOP
))
update_transtable
(
bat_priv
,
orig_node
,
tt_buff
,
tt_update_orig
(
bat_priv
,
orig_node
,
tt_buff
,
batman_packet
->
tt_num_changes
,
batman_packet
->
ttvn
,
batman_packet
->
tt_crc
);
batman_packet
->
ttvn
,
batman_packet
->
tt_crc
);
if
(
orig_node
->
gw_flags
!=
batman_packet
->
gw_flags
)
gw_node_update
(
bat_priv
,
orig_node
,
batman_packet
->
gw_flags
);
...
...
@@ -1243,7 +1182,7 @@ int recv_tt_query(struct sk_buff *skb, struct hard_iface *recv_if)
}
break
;
case
TT_RESPONSE
:
/* packet needs to be lineari
s
ed to access the TT changes */
/* packet needs to be lineari
z
ed to access the TT changes */
if
(
skb_linearize
(
skb
)
<
0
)
goto
out
;
...
...
@@ -1300,7 +1239,7 @@ int recv_roam_adv(struct sk_buff *skb, struct hard_iface *recv_if)
roam_adv_packet
->
client
);
tt_global_add
(
bat_priv
,
orig_node
,
roam_adv_packet
->
client
,
atomic_read
(
&
orig_node
->
last_ttvn
)
+
1
,
true
);
atomic_read
(
&
orig_node
->
last_ttvn
)
+
1
,
true
,
false
);
/* Roaming phase starts: I have new information but the ttvn has not
* been incremented yet. This flag will make me check all the incoming
...
...
@@ -1536,7 +1475,7 @@ static int check_unicast_ttvn(struct bat_priv *bat_priv,
ethhdr
=
(
struct
ethhdr
*
)(
skb
->
data
+
sizeof
(
struct
unicast_packet
));
orig_node
=
transtable_search
(
bat_priv
,
ethhdr
->
h_dest
);
orig_node
=
transtable_search
(
bat_priv
,
NULL
,
ethhdr
->
h_dest
);
if
(
!
orig_node
)
{
if
(
!
is_my_client
(
bat_priv
,
ethhdr
->
h_dest
))
...
...
net/batman-adv/send.c
View file @
0e43182c
...
...
@@ -135,7 +135,7 @@ static void send_packet_to_if(struct forw_packet *forw_packet,
"Forwarding"
));
bat_dbg
(
DBG_BATMAN
,
bat_priv
,
"%s %spacket (originator %pM, seqno %d, TQ %d, TTL %d,"
" IDF %s,
h
vn %d) on interface %s [%pM]
\n
"
,
" IDF %s,
tt
vn %d) on interface %s [%pM]
\n
"
,
fwd_str
,
(
packet_num
>
0
?
"aggregated "
:
""
),
batman_packet
->
orig
,
ntohl
(
batman_packet
->
seqno
),
batman_packet
->
tq
,
batman_packet
->
ttl
,
...
...
@@ -313,7 +313,7 @@ void schedule_own_packet(struct hard_iface *hard_iface)
prepare_packet_buffer
(
bat_priv
,
hard_iface
);
}
/* if the changes have been sent
enough times
*/
/* if the changes have been sent
often enough
*/
if
(
!
atomic_dec_not_zero
(
&
bat_priv
->
tt_ogm_append_cnt
))
reset_packet_buffer
(
bat_priv
,
hard_iface
);
}
...
...
@@ -454,7 +454,7 @@ static void _add_bcast_packet_to_list(struct bat_priv *bat_priv,
}
/* add a broadcast packet to the queue and setup timers. broadcast packets
* are sent multiple times to increase probability for be
e
ing received.
* are sent multiple times to increase probability for being received.
*
* This function returns NETDEV_TX_OK on success and NETDEV_TX_BUSY on
* errors.
...
...
@@ -612,7 +612,7 @@ void purge_outstanding_packets(struct bat_priv *bat_priv,
&
bat_priv
->
forw_bcast_list
,
list
)
{
/**
* if purge_outstanding_packets() was called with an arg
m
ument
* if purge_outstanding_packets() was called with an argument
* we delete only packets belonging to the given interface
*/
if
((
hard_iface
)
&&
...
...
@@ -641,7 +641,7 @@ void purge_outstanding_packets(struct bat_priv *bat_priv,
&
bat_priv
->
forw_bat_list
,
list
)
{
/**
* if purge_outstanding_packets() was called with an arg
m
ument
* if purge_outstanding_packets() was called with an argument
* we delete only packets belonging to the given interface
*/
if
((
hard_iface
)
&&
...
...
net/batman-adv/soft-interface.c
View file @
0e43182c
...
...
@@ -532,11 +532,11 @@ static int interface_set_mac_addr(struct net_device *dev, void *p)
if
(
!
is_valid_ether_addr
(
addr
->
sa_data
))
return
-
EADDRNOTAVAIL
;
/* only modify transtable if it has been initiali
s
ed before */
/* only modify transtable if it has been initiali
z
ed before */
if
(
atomic_read
(
&
bat_priv
->
mesh_state
)
==
MESH_ACTIVE
)
{
tt_local_remove
(
bat_priv
,
dev
->
dev_addr
,
"mac address changed"
,
false
);
tt_local_add
(
dev
,
addr
->
sa_data
);
tt_local_add
(
dev
,
addr
->
sa_data
,
NULL_IFINDEX
);
}
memcpy
(
dev
->
dev_addr
,
addr
->
sa_data
,
ETH_ALEN
);
...
...
@@ -595,9 +595,10 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
goto
dropped
;
/* Register the client MAC in the transtable */
tt_local_add
(
soft_iface
,
ethhdr
->
h_source
);
tt_local_add
(
soft_iface
,
ethhdr
->
h_source
,
skb
->
skb_iif
);
orig_node
=
transtable_search
(
bat_priv
,
ethhdr
->
h_dest
);
orig_node
=
transtable_search
(
bat_priv
,
ethhdr
->
h_source
,
ethhdr
->
h_dest
);
if
(
is_multicast_ether_addr
(
ethhdr
->
h_dest
)
||
(
orig_node
&&
orig_node
->
gw_flags
))
{
ret
=
gw_is_target
(
bat_priv
,
skb
,
orig_node
);
...
...
@@ -739,6 +740,9 @@ void interface_rx(struct net_device *soft_iface,
soft_iface
->
last_rx
=
jiffies
;
if
(
is_ap_isolated
(
bat_priv
,
ethhdr
->
h_source
,
ethhdr
->
h_dest
))
goto
dropped
;
netif_rx
(
skb
);
goto
out
;
...
...
@@ -812,6 +816,7 @@ struct net_device *softif_create(const char *name)
atomic_set
(
&
bat_priv
->
aggregated_ogms
,
1
);
atomic_set
(
&
bat_priv
->
bonding
,
0
);
atomic_set
(
&
bat_priv
->
ap_isolation
,
0
);
atomic_set
(
&
bat_priv
->
vis_mode
,
VIS_TYPE_CLIENT_UPDATE
);
atomic_set
(
&
bat_priv
->
gw_mode
,
GW_MODE_OFF
);
atomic_set
(
&
bat_priv
->
gw_sel_class
,
20
);
...
...
net/batman-adv/translation-table.c
View file @
0e43182c
...
...
@@ -183,7 +183,8 @@ static int tt_local_init(struct bat_priv *bat_priv)
return
1
;
}
void
tt_local_add
(
struct
net_device
*
soft_iface
,
const
uint8_t
*
addr
)
void
tt_local_add
(
struct
net_device
*
soft_iface
,
const
uint8_t
*
addr
,
int
ifindex
)
{
struct
bat_priv
*
bat_priv
=
netdev_priv
(
soft_iface
);
struct
tt_local_entry
*
tt_local_entry
=
NULL
;
...
...
@@ -207,6 +208,8 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr)
memcpy
(
tt_local_entry
->
addr
,
addr
,
ETH_ALEN
);
tt_local_entry
->
last_seen
=
jiffies
;
tt_local_entry
->
flags
=
NO_FLAGS
;
if
(
is_wifi_iface
(
ifindex
))
tt_local_entry
->
flags
|=
TT_CLIENT_WIFI
;
atomic_set
(
&
tt_local_entry
->
refcount
,
2
);
/* the batman interface mac address should never be purged */
...
...
@@ -329,7 +332,7 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset)
rcu_read_lock
();
__hlist_for_each_rcu
(
node
,
head
)
buf_size
+=
2
1
;
buf_size
+=
2
9
;
rcu_read_unlock
();
}
...
...
@@ -348,8 +351,19 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset)
rcu_read_lock
();
hlist_for_each_entry_rcu
(
tt_local_entry
,
node
,
head
,
hash_entry
)
{
pos
+=
snprintf
(
buff
+
pos
,
22
,
" * %pM
\n
"
,
tt_local_entry
->
addr
);
pos
+=
snprintf
(
buff
+
pos
,
30
,
" * %pM "
"[%c%c%c%c%c]
\n
"
,
tt_local_entry
->
addr
,
(
tt_local_entry
->
flags
&
TT_CLIENT_ROAM
?
'R'
:
'.'
),
(
tt_local_entry
->
flags
&
TT_CLIENT_NOPURGE
?
'P'
:
'.'
),
(
tt_local_entry
->
flags
&
TT_CLIENT_NEW
?
'N'
:
'.'
),
(
tt_local_entry
->
flags
&
TT_CLIENT_PENDING
?
'X'
:
'.'
),
(
tt_local_entry
->
flags
&
TT_CLIENT_WIFI
?
'W'
:
'.'
));
}
rcu_read_unlock
();
}
...
...
@@ -369,8 +383,8 @@ static void tt_local_set_pending(struct bat_priv *bat_priv,
tt_local_event
(
bat_priv
,
tt_local_entry
->
addr
,
tt_local_entry
->
flags
|
flags
);
/* The local client has to be m
e
rked as "pending to be removed" but has
* to be kept in the table in order to send it in a
n full tables
/* The local client has to be m
a
rked as "pending to be removed" but has
* to be kept in the table in order to send it in a
full table
* response issued before the net ttvn increment (consistency check) */
tt_local_entry
->
flags
|=
TT_CLIENT_PENDING
;
}
...
...
@@ -495,7 +509,8 @@ static void tt_changes_list_free(struct bat_priv *bat_priv)
/* caller must hold orig_node refcount */
int
tt_global_add
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
,
const
unsigned
char
*
tt_addr
,
uint8_t
ttvn
,
bool
roaming
)
const
unsigned
char
*
tt_addr
,
uint8_t
ttvn
,
bool
roaming
,
bool
wifi
)
{
struct
tt_global_entry
*
tt_global_entry
;
struct
orig_node
*
orig_node_tmp
;
...
...
@@ -537,6 +552,9 @@ int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node,
tt_global_entry
->
roam_at
=
0
;
}
if
(
wifi
)
tt_global_entry
->
flags
|=
TT_CLIENT_WIFI
;
bat_dbg
(
DBG_TT
,
bat_priv
,
"Creating new global tt entry: %pM (via %pM)
\n
"
,
tt_global_entry
->
addr
,
orig_node
->
orig
);
...
...
@@ -582,8 +600,8 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset)
seq_printf
(
seq
,
"Globally announced TT entries received via the mesh %s
\n
"
,
net_dev
->
name
);
seq_printf
(
seq
,
" %-13s %s %-15s %s
\n
"
,
"Client"
,
"(TTVN)"
,
"Originator"
,
"(Curr TTVN)"
);
seq_printf
(
seq
,
" %-13s %s %-15s %s
%s
\n
"
,
"Client"
,
"(TTVN)"
,
"Originator"
,
"(Curr TTVN)"
,
"Flags"
);
buf_size
=
1
;
/* Estimate length for: " * xx:xx:xx:xx:xx:xx (ttvn) via
...
...
@@ -593,7 +611,7 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset)
rcu_read_lock
();
__hlist_for_each_rcu
(
node
,
head
)
buf_size
+=
59
;
buf_size
+=
67
;
rcu_read_unlock
();
}
...
...
@@ -612,14 +630,20 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset)
rcu_read_lock
();
hlist_for_each_entry_rcu
(
tt_global_entry
,
node
,
head
,
hash_entry
)
{
pos
+=
snprintf
(
buff
+
pos
,
6
1
,
" * %pM (%3u) via %pM (%3u)
\n
"
,
tt_global_entry
->
addr
,
pos
+=
snprintf
(
buff
+
pos
,
6
9
,
" * %pM (%3u) via %pM (%3u)
"
"[%c%c%c]
\n
"
,
tt_global_entry
->
addr
,
tt_global_entry
->
ttvn
,
tt_global_entry
->
orig_node
->
orig
,
(
uint8_t
)
atomic_read
(
&
tt_global_entry
->
orig_node
->
last_ttvn
));
last_ttvn
),
(
tt_global_entry
->
flags
&
TT_CLIENT_ROAM
?
'R'
:
'.'
),
(
tt_global_entry
->
flags
&
TT_CLIENT_PENDING
?
'X'
:
'.'
),
(
tt_global_entry
->
flags
&
TT_CLIENT_WIFI
?
'W'
:
'.'
));
}
rcu_read_unlock
();
}
...
...
@@ -774,30 +798,56 @@ static void tt_global_table_free(struct bat_priv *bat_priv)
bat_priv
->
tt_global_hash
=
NULL
;
}
static
bool
_is_ap_isolated
(
struct
tt_local_entry
*
tt_local_entry
,
struct
tt_global_entry
*
tt_global_entry
)
{
bool
ret
=
false
;
if
(
tt_local_entry
->
flags
&
TT_CLIENT_WIFI
&&
tt_global_entry
->
flags
&
TT_CLIENT_WIFI
)
ret
=
true
;
return
ret
;
}
struct
orig_node
*
transtable_search
(
struct
bat_priv
*
bat_priv
,
const
uint8_t
*
addr
)
const
uint8_t
*
src
,
const
uint8_t
*
addr
)
{
struct
tt_global_entry
*
tt_global_entry
;
struct
tt_local_entry
*
tt_local_entry
=
NULL
;
struct
tt_global_entry
*
tt_global_entry
=
NULL
;
struct
orig_node
*
orig_node
=
NULL
;
tt_global_entry
=
tt_global_hash_find
(
bat_priv
,
addr
);
if
(
src
&&
atomic_read
(
&
bat_priv
->
ap_isolation
))
{
tt_local_entry
=
tt_local_hash_find
(
bat_priv
,
src
);
if
(
!
tt_local_entry
)
goto
out
;
}
tt_global_entry
=
tt_global_hash_find
(
bat_priv
,
addr
);
if
(
!
tt_global_entry
)
goto
out
;
/* check whether the clients should not communicate due to AP
* isolation */
if
(
tt_local_entry
&&
_is_ap_isolated
(
tt_local_entry
,
tt_global_entry
))
goto
out
;
if
(
!
atomic_inc_not_zero
(
&
tt_global_entry
->
orig_node
->
refcount
))
goto
free_t
t
;
goto
ou
t
;
/* A global client marked as PENDING has already moved from that
* originator */
if
(
tt_global_entry
->
flags
&
TT_CLIENT_PENDING
)
goto
free_t
t
;
goto
ou
t
;
orig_node
=
tt_global_entry
->
orig_node
;
free_tt:
tt_global_entry_free_ref
(
tt_global_entry
);
out:
if
(
tt_global_entry
)
tt_global_entry_free_ref
(
tt_global_entry
);
if
(
tt_local_entry
)
tt_local_entry_free_ref
(
tt_local_entry
);
return
orig_node
;
}
...
...
@@ -1029,7 +1079,8 @@ static struct sk_buff *tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
return
skb
;
}
int
send_tt_request
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
dst_orig_node
,
static
int
send_tt_request
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
dst_orig_node
,
uint8_t
ttvn
,
uint16_t
tt_crc
,
bool
full_table
)
{
struct
sk_buff
*
skb
=
NULL
;
...
...
@@ -1137,12 +1188,12 @@ static bool send_other_tt_response(struct bat_priv *bat_priv,
orig_ttvn
=
(
uint8_t
)
atomic_read
(
&
req_dst_orig_node
->
last_ttvn
);
req_ttvn
=
tt_request
->
ttvn
;
/* I
have not
the requested data */
/* I
don't have
the requested data */
if
(
orig_ttvn
!=
req_ttvn
||
tt_request
->
tt_data
!=
req_dst_orig_node
->
tt_crc
)
goto
out
;
/* If
it has explicitly been requested the full table
*/
/* If
the full table has been explicitly requested
*/
if
(
tt_request
->
flags
&
TT_FULL_TABLE
||
!
req_dst_orig_node
->
tt_buff
)
full_table
=
true
;
...
...
@@ -1363,7 +1414,9 @@ static void _tt_update_changes(struct bat_priv *bat_priv,
(
tt_change
+
i
)
->
flags
&
TT_CLIENT_ROAM
);
else
if
(
!
tt_global_add
(
bat_priv
,
orig_node
,
(
tt_change
+
i
)
->
addr
,
ttvn
,
false
))
(
tt_change
+
i
)
->
addr
,
ttvn
,
false
,
(
tt_change
+
i
)
->
flags
&
TT_CLIENT_WIFI
))
/* In case of problem while storing a
* global_entry, we stop the updating
* procedure without committing the
...
...
@@ -1403,7 +1456,8 @@ static void tt_fill_gtable(struct bat_priv *bat_priv,
orig_node_free_ref
(
orig_node
);
}
void
tt_update_changes
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
,
static
void
tt_update_changes
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
,
uint16_t
tt_num_changes
,
uint8_t
ttvn
,
struct
tt_change
*
tt_change
)
{
...
...
@@ -1720,3 +1774,90 @@ void tt_commit_changes(struct bat_priv *bat_priv)
atomic_inc
(
&
bat_priv
->
ttvn
);
bat_priv
->
tt_poss_change
=
false
;
}
bool
is_ap_isolated
(
struct
bat_priv
*
bat_priv
,
uint8_t
*
src
,
uint8_t
*
dst
)
{
struct
tt_local_entry
*
tt_local_entry
=
NULL
;
struct
tt_global_entry
*
tt_global_entry
=
NULL
;
bool
ret
=
true
;
if
(
!
atomic_read
(
&
bat_priv
->
ap_isolation
))
return
false
;
tt_local_entry
=
tt_local_hash_find
(
bat_priv
,
dst
);
if
(
!
tt_local_entry
)
goto
out
;
tt_global_entry
=
tt_global_hash_find
(
bat_priv
,
src
);
if
(
!
tt_global_entry
)
goto
out
;
if
(
_is_ap_isolated
(
tt_local_entry
,
tt_global_entry
))
goto
out
;
ret
=
false
;
out:
if
(
tt_global_entry
)
tt_global_entry_free_ref
(
tt_global_entry
);
if
(
tt_local_entry
)
tt_local_entry_free_ref
(
tt_local_entry
);
return
ret
;
}
void
tt_update_orig
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
,
const
unsigned
char
*
tt_buff
,
uint8_t
tt_num_changes
,
uint8_t
ttvn
,
uint16_t
tt_crc
)
{
uint8_t
orig_ttvn
=
(
uint8_t
)
atomic_read
(
&
orig_node
->
last_ttvn
);
bool
full_table
=
true
;
/* the ttvn increased by one -> we can apply the attached changes */
if
(
ttvn
-
orig_ttvn
==
1
)
{
/* the OGM could not contain the changes due to their size or
* because they have already been sent TT_OGM_APPEND_MAX times.
* In this case send a tt request */
if
(
!
tt_num_changes
)
{
full_table
=
false
;
goto
request_table
;
}
tt_update_changes
(
bat_priv
,
orig_node
,
tt_num_changes
,
ttvn
,
(
struct
tt_change
*
)
tt_buff
);
/* Even if we received the precomputed crc with the OGM, we
* prefer to recompute it to spot any possible inconsistency
* in the global table */
orig_node
->
tt_crc
=
tt_global_crc
(
bat_priv
,
orig_node
);
/* The ttvn alone is not enough to guarantee consistency
* because a single value could represent different states
* (due to the wrap around). Thus a node has to check whether
* the resulting table (after applying the changes) is still
* consistent or not. E.g. a node could disconnect while its
* ttvn is X and reconnect on ttvn = X + TTVN_MAX: in this case
* checking the CRC value is mandatory to detect the
* inconsistency */
if
(
orig_node
->
tt_crc
!=
tt_crc
)
goto
request_table
;
/* Roaming phase is over: tables are in sync again. I can
* unset the flag */
orig_node
->
tt_poss_change
=
false
;
}
else
{
/* if we missed more than one change or our tables are not
* in sync anymore -> request fresh tt data */
if
(
ttvn
!=
orig_ttvn
||
orig_node
->
tt_crc
!=
tt_crc
)
{
request_table:
bat_dbg
(
DBG_TT
,
bat_priv
,
"TT inconsistency for %pM. "
"Need to retrieve the correct information "
"(ttvn: %u last_ttvn: %u crc: %u last_crc: "
"%u num_changes: %u)
\n
"
,
orig_node
->
orig
,
ttvn
,
orig_ttvn
,
tt_crc
,
orig_node
->
tt_crc
,
tt_num_changes
);
send_tt_request
(
bat_priv
,
orig_node
,
ttvn
,
tt_crc
,
full_table
);
return
;
}
}
}
net/batman-adv/translation-table.h
View file @
0e43182c
...
...
@@ -26,15 +26,16 @@ int tt_len(int changes_num);
int
tt_changes_fill_buffer
(
struct
bat_priv
*
bat_priv
,
unsigned
char
*
buff
,
int
buff_len
);
int
tt_init
(
struct
bat_priv
*
bat_priv
);
void
tt_local_add
(
struct
net_device
*
soft_iface
,
const
uint8_t
*
addr
);
void
tt_local_add
(
struct
net_device
*
soft_iface
,
const
uint8_t
*
addr
,
int
ifindex
);
void
tt_local_remove
(
struct
bat_priv
*
bat_priv
,
const
uint8_t
*
addr
,
const
char
*
message
,
bool
roaming
);
int
tt_local_seq_print_text
(
struct
seq_file
*
seq
,
void
*
offset
);
void
tt_global_add_orig
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
,
const
unsigned
char
*
tt_buff
,
int
tt_buff_len
);
int
tt_global_add
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
,
const
unsigned
char
*
addr
,
uint8_t
ttvn
,
bool
roaming
);
int
tt_global_add
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
,
const
unsigned
char
*
addr
,
uint8_t
ttvn
,
bool
roaming
,
bool
wifi
);
int
tt_global_seq_print_text
(
struct
seq_file
*
seq
,
void
*
offset
);
void
tt_global_del_orig
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
,
const
char
*
message
);
...
...
@@ -42,25 +43,23 @@ void tt_global_del(struct bat_priv *bat_priv,
struct
orig_node
*
orig_node
,
const
unsigned
char
*
addr
,
const
char
*
message
,
bool
roaming
);
struct
orig_node
*
transtable_search
(
struct
bat_priv
*
bat_priv
,
const
uint8_t
*
addr
);
const
uint8_t
*
src
,
const
uint8_t
*
addr
);
void
tt_save_orig_buffer
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
,
const
unsigned
char
*
tt_buff
,
uint8_t
tt_num_changes
);
uint16_t
tt_local_crc
(
struct
bat_priv
*
bat_priv
);
uint16_t
tt_global_crc
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
);
void
tt_free
(
struct
bat_priv
*
bat_priv
);
int
send_tt_request
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
dst_orig_node
,
uint8_t
hvn
,
uint16_t
tt_crc
,
bool
full_table
);
bool
send_tt_response
(
struct
bat_priv
*
bat_priv
,
struct
tt_query_packet
*
tt_request
);
void
tt_update_changes
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
,
uint16_t
tt_num_changes
,
uint8_t
ttvn
,
struct
tt_change
*
tt_change
);
bool
is_my_client
(
struct
bat_priv
*
bat_priv
,
const
uint8_t
*
addr
);
void
handle_tt_response
(
struct
bat_priv
*
bat_priv
,
struct
tt_query_packet
*
tt_response
);
void
send_roam_adv
(
struct
bat_priv
*
bat_priv
,
uint8_t
*
client
,
struct
orig_node
*
orig_node
);
void
tt_commit_changes
(
struct
bat_priv
*
bat_priv
);
bool
is_ap_isolated
(
struct
bat_priv
*
bat_priv
,
uint8_t
*
src
,
uint8_t
*
dst
);
void
tt_update_orig
(
struct
bat_priv
*
bat_priv
,
struct
orig_node
*
orig_node
,
const
unsigned
char
*
tt_buff
,
uint8_t
tt_num_changes
,
uint8_t
ttvn
,
uint16_t
tt_crc
);
#endif
/* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */
net/batman-adv/types.h
View file @
0e43182c
...
...
@@ -57,7 +57,7 @@ struct hard_iface {
* @batman_seqno_reset: time when the batman seqno window was reset
* @gw_flags: flags related to gateway class
* @flags: for now only VIS_SERVER flag
* @last_real_seqno: last and best known squence number
* @last_real_seqno: last and best known s
e
quence number
* @last_ttl: ttl of last received packet
* @last_bcast_seqno: last broadcast sequence number received by this host
*
...
...
@@ -146,6 +146,7 @@ struct bat_priv {
atomic_t
aggregated_ogms
;
/* boolean */
atomic_t
bonding
;
/* boolean */
atomic_t
fragmentation
;
/* boolean */
atomic_t
ap_isolation
;
/* boolean */
atomic_t
vis_mode
;
/* VIS_TYPE_* */
atomic_t
gw_mode
;
/* GW_MODE_* */
atomic_t
gw_sel_class
;
/* uint */
...
...
@@ -156,7 +157,7 @@ struct bat_priv {
atomic_t
bcast_seqno
;
atomic_t
bcast_queue_left
;
atomic_t
batman_queue_left
;
atomic_t
ttvn
;
/* tranlation table version number */
atomic_t
ttvn
;
/* tran
s
lation table version number */
atomic_t
tt_ogm_append_cnt
;
atomic_t
tt_local_changes
;
/* changes registered in a OGM interval */
/* The tt_poss_change flag is used to detect an ongoing roaming phase.
...
...
net/batman-adv/unicast.c
View file @
0e43182c
...
...
@@ -299,8 +299,10 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv)
goto
find_router
;
}
/* check for tt host - increases orig_node refcount */
orig_node
=
transtable_search
(
bat_priv
,
ethhdr
->
h_dest
);
/* check for tt host - increases orig_node refcount.
* returns NULL in case of AP isolation */
orig_node
=
transtable_search
(
bat_priv
,
ethhdr
->
h_source
,
ethhdr
->
h_dest
);
find_router:
/**
...
...
net/batman-adv/unicast.h
View file @
0e43182c
...
...
@@ -24,7 +24,7 @@
#include "packet.h"
#define FRAG_TIMEOUT 10000
/* purge frag list entr
y
s after time in ms */
#define FRAG_TIMEOUT 10000
/* purge frag list entr
ie
s after time in ms */
#define FRAG_BUFFER_SIZE 6
/* number of list elements in buffer */
int
frag_reassemble_skb
(
struct
sk_buff
*
skb
,
struct
bat_priv
*
bat_priv
,
...
...
net/batman-adv/vis.c
View file @
0e43182c
...
...
@@ -131,7 +131,7 @@ static void vis_data_insert_interface(const uint8_t *interface,
return
;
}
/* its a new address, add it to the list */
/* it
'
s a new address, add it to the list */
entry
=
kmalloc
(
sizeof
(
*
entry
),
GFP_ATOMIC
);
if
(
!
entry
)
return
;
...
...
@@ -465,7 +465,7 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv,
/* try to add it */
hash_added
=
hash_add
(
bat_priv
->
vis_hash
,
vis_info_cmp
,
vis_info_choose
,
info
,
&
info
->
hash_entry
);
if
(
hash_added
<
0
)
{
if
(
hash_added
!=
0
)
{
/* did not work (for some reason) */
kref_put
(
&
info
->
refcount
,
free_info
);
info
=
NULL
;
...
...
@@ -920,7 +920,7 @@ int vis_init(struct bat_priv *bat_priv)
hash_added
=
hash_add
(
bat_priv
->
vis_hash
,
vis_info_cmp
,
vis_info_choose
,
bat_priv
->
my_vis_info
,
&
bat_priv
->
my_vis_info
->
hash_entry
);
if
(
hash_added
<
0
)
{
if
(
hash_added
!=
0
)
{
pr_err
(
"Can't add own vis packet into hash
\n
"
);
/* not in hash, need to remove it manually. */
kref_put
(
&
bat_priv
->
my_vis_info
->
refcount
,
free_info
);
...
...
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