Commit 2ec600d6 authored by Luis Carlos Cobo's avatar Luis Carlos Cobo Committed by John W. Linville

nl80211/cfg80211: support for mesh, sta dumping

Added support for mesh id and mesh path operation as well as
station structure dumping.
Signed-off-by: default avatarLuis Carlos Cobo <luisca@cozybit.com>
Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent cc0672a1
...@@ -78,6 +78,18 @@ ...@@ -78,6 +78,18 @@
* or, if no MAC address given, all stations, on the interface identified * or, if no MAC address given, all stations, on the interface identified
* by %NL80211_ATTR_IFINDEX. * by %NL80211_ATTR_IFINDEX.
* *
* @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
* destination %NL80211_ATTR_MAC on the interface identified by
* %NL80211_ATTR_IFINDEX.
* @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to
* destination %NL80211_ATTR_MAC on the interface identified by
* %NL80211_ATTR_IFINDEX.
* @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
* the interface identified by %NL80211_ATTR_IFINDEX.
* @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
* or, if no MAC address given, all mesh paths, on the interface identified
* by %NL80211_ATTR_IFINDEX.
*
* @NL80211_CMD_MAX: highest used command number * @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use * @__NL80211_CMD_AFTER_LAST: internal use
*/ */
...@@ -112,6 +124,11 @@ enum nl80211_commands { ...@@ -112,6 +124,11 @@ enum nl80211_commands {
/* add commands here */ /* add commands here */
NL80211_CMD_GET_MPATH,
NL80211_CMD_SET_MPATH,
NL80211_CMD_NEW_MPATH,
NL80211_CMD_DEL_MPATH,
/* used to define NL80211_CMD_MAX below */ /* used to define NL80211_CMD_MAX below */
__NL80211_CMD_AFTER_LAST, __NL80211_CMD_AFTER_LAST,
NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
...@@ -157,13 +174,21 @@ enum nl80211_commands { ...@@ -157,13 +174,21 @@ enum nl80211_commands {
* restriction (at most %NL80211_MAX_SUPP_RATES). * restriction (at most %NL80211_MAX_SUPP_RATES).
* @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station
* to, or the AP interface the station was originally added to to. * to, or the AP interface the station was originally added to to.
* @NL80211_ATTR_STA_STATS: statistics for a station, part of station info * @NL80211_ATTR_STA_INFO: information about a station, part of station info
* given for %NL80211_CMD_GET_STATION, nested attribute containing * given for %NL80211_CMD_GET_STATION, nested attribute containing
* info as possible, see &enum nl80211_sta_stats. * info as possible, see &enum nl80211_sta_info.
* *
* @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands, * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands,
* consisting of a nested array. * consisting of a nested array.
* *
* @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
* @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link.
* @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
* @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
* info given for %NL80211_CMD_GET_MPATH, nested attribute described at
* &enum nl80211_mpath_info.
*
*
* @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of
* &enum nl80211_mntr_flags. * &enum nl80211_mntr_flags.
* *
...@@ -199,7 +224,7 @@ enum nl80211_attrs { ...@@ -199,7 +224,7 @@ enum nl80211_attrs {
NL80211_ATTR_STA_LISTEN_INTERVAL, NL80211_ATTR_STA_LISTEN_INTERVAL,
NL80211_ATTR_STA_SUPPORTED_RATES, NL80211_ATTR_STA_SUPPORTED_RATES,
NL80211_ATTR_STA_VLAN, NL80211_ATTR_STA_VLAN,
NL80211_ATTR_STA_STATS, NL80211_ATTR_STA_INFO,
NL80211_ATTR_WIPHY_BANDS, NL80211_ATTR_WIPHY_BANDS,
...@@ -207,6 +232,11 @@ enum nl80211_attrs { ...@@ -207,6 +232,11 @@ enum nl80211_attrs {
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
NL80211_ATTR_MESH_ID,
NL80211_ATTR_STA_PLINK_ACTION,
NL80211_ATTR_MPATH_NEXT_HOP,
NL80211_ATTR_MPATH_INFO,
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
}; };
...@@ -223,6 +253,7 @@ enum nl80211_attrs { ...@@ -223,6 +253,7 @@ enum nl80211_attrs {
* @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points
* @NL80211_IFTYPE_WDS: wireless distribution interface * @NL80211_IFTYPE_WDS: wireless distribution interface
* @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
* @NL80211_IFTYPE_MESH_POINT: mesh point
* @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NL80211_IFTYPE_MAX: highest interface type number currently defined
* @__NL80211_IFTYPE_AFTER_LAST: internal use * @__NL80211_IFTYPE_AFTER_LAST: internal use
* *
...@@ -238,6 +269,7 @@ enum nl80211_iftype { ...@@ -238,6 +269,7 @@ enum nl80211_iftype {
NL80211_IFTYPE_AP_VLAN, NL80211_IFTYPE_AP_VLAN,
NL80211_IFTYPE_WDS, NL80211_IFTYPE_WDS,
NL80211_IFTYPE_MONITOR, NL80211_IFTYPE_MONITOR,
NL80211_IFTYPE_MESH_POINT,
/* keep last */ /* keep last */
__NL80211_IFTYPE_AFTER_LAST, __NL80211_IFTYPE_AFTER_LAST,
...@@ -267,27 +299,78 @@ enum nl80211_sta_flags { ...@@ -267,27 +299,78 @@ enum nl80211_sta_flags {
}; };
/** /**
* enum nl80211_sta_stats - station statistics * enum nl80211_sta_info - station information
* *
* These attribute types are used with %NL80211_ATTR_STA_STATS * These attribute types are used with %NL80211_ATTR_STA_INFO
* when getting information about a station. * when getting information about a station.
* *
* @__NL80211_STA_STAT_INVALID: attribute number 0 is reserved * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved
* @NL80211_STA_STAT_INACTIVE_TIME: time since last activity (u32, msecs) * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
* @NL80211_STA_STAT_RX_BYTES: total received bytes (u32, from this station) * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station)
* @NL80211_STA_STAT_TX_BYTES: total transmitted bytes (u32, to this station) * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
* @__NL80211_STA_STAT_AFTER_LAST: internal * @__NL80211_STA_INFO_AFTER_LAST: internal
* @NL80211_STA_STAT_MAX: highest possible station stats attribute * @NL80211_STA_INFO_MAX: highest possible station info attribute
*/
enum nl80211_sta_info {
__NL80211_STA_INFO_INVALID,
NL80211_STA_INFO_INACTIVE_TIME,
NL80211_STA_INFO_RX_BYTES,
NL80211_STA_INFO_TX_BYTES,
NL80211_STA_INFO_LLID,
NL80211_STA_INFO_PLID,
NL80211_STA_INFO_PLINK_STATE,
/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1
};
/**
* enum nl80211_mpath_flags - nl80211 mesh path flags
*
* @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active
* @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running
* @NL80211_MPATH_FLAG_DSN_VALID: the mesh path contains a valid DSN
* @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set
* @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded
*/
enum nl80211_mpath_flags {
NL80211_MPATH_FLAG_ACTIVE = 1<<0,
NL80211_MPATH_FLAG_RESOLVING = 1<<1,
NL80211_MPATH_FLAG_DSN_VALID = 1<<2,
NL80211_MPATH_FLAG_FIXED = 1<<3,
NL80211_MPATH_FLAG_RESOLVED = 1<<4,
};
/**
* enum nl80211_mpath_info - mesh path information
*
* These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting
* information about a mesh path.
*
* @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved
* @NL80211_ATTR_MPATH_FRAME_QLEN: number of queued frames for this destination
* @NL80211_ATTR_MPATH_DSN: destination sequence number
* @NL80211_ATTR_MPATH_METRIC: metric (cost) of this mesh path
* @NL80211_ATTR_MPATH_EXPTIME: expiration time for the path, in msec from now
* @NL80211_ATTR_MPATH_FLAGS: mesh path flags, enumerated in
* &enum nl80211_mpath_flags;
* @NL80211_ATTR_MPATH_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
* @NL80211_ATTR_MPATH_DISCOVERY_RETRIES: mesh path discovery retries
*/ */
enum nl80211_sta_stats { enum nl80211_mpath_info {
__NL80211_STA_STAT_INVALID, __NL80211_MPATH_INFO_INVALID,
NL80211_STA_STAT_INACTIVE_TIME, NL80211_MPATH_INFO_FRAME_QLEN,
NL80211_STA_STAT_RX_BYTES, NL80211_MPATH_INFO_DSN,
NL80211_STA_STAT_TX_BYTES, NL80211_MPATH_INFO_METRIC,
NL80211_MPATH_INFO_EXPTIME,
NL80211_MPATH_INFO_FLAGS,
NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
NL80211_MPATH_INFO_DISCOVERY_RETRIES,
/* keep last */ /* keep last */
__NL80211_STA_STAT_AFTER_LAST, __NL80211_MPATH_INFO_AFTER_LAST,
NL80211_STA_STAT_MAX = __NL80211_STA_STAT_AFTER_LAST - 1 NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1
}; };
/** /**
......
...@@ -12,6 +12,16 @@ ...@@ -12,6 +12,16 @@
* Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net> * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
*/ */
/**
* struct vif_params - describes virtual interface parameters
* @mesh_id: mesh ID to use
* @mesh_id_len: length of the mesh ID
*/
struct vif_params {
u8 *mesh_id;
int mesh_id_len;
};
/* Radiotap header iteration /* Radiotap header iteration
* implemented in net/wireless/radiotap.c * implemented in net/wireless/radiotap.c
* docs in Documentation/networking/radiotap-headers.txt * docs in Documentation/networking/radiotap-headers.txt
...@@ -108,6 +118,19 @@ enum station_flags { ...@@ -108,6 +118,19 @@ enum station_flags {
STATION_FLAG_WME = 1<<NL80211_STA_FLAG_WME, STATION_FLAG_WME = 1<<NL80211_STA_FLAG_WME,
}; };
/**
* enum plink_action - actions to perform in mesh peers
*
* @PLINK_ACTION_INVALID: action 0 is reserved
* @PLINK_ACTION_OPEN: start mesh peer link establishment
* @PLINK_ACTION_BLOCL: block traffic from this mesh peer
*/
enum plink_actions {
PLINK_ACTION_INVALID,
PLINK_ACTION_OPEN,
PLINK_ACTION_BLOCK,
};
/** /**
* struct station_parameters - station parameters * struct station_parameters - station parameters
* *
...@@ -128,39 +151,52 @@ struct station_parameters { ...@@ -128,39 +151,52 @@ struct station_parameters {
int listen_interval; int listen_interval;
u16 aid; u16 aid;
u8 supported_rates_len; u8 supported_rates_len;
u8 plink_action;
}; };
/** /**
* enum station_stats_flags - station statistics flags * enum station_info_flags - station information flags
* *
* Used by the driver to indicate which info in &struct station_stats * Used by the driver to indicate which info in &struct station_info
* it has filled in during get_station(). * it has filled in during get_station() or dump_station().
* *
* @STATION_STAT_INACTIVE_TIME: @inactive_time filled * @STATION_INFO_INACTIVE_TIME: @inactive_time filled
* @STATION_STAT_RX_BYTES: @rx_bytes filled * @STATION_INFO_RX_BYTES: @rx_bytes filled
* @STATION_STAT_TX_BYTES: @tx_bytes filled * @STATION_INFO_TX_BYTES: @tx_bytes filled
* @STATION_INFO_LLID: @llid filled
* @STATION_INFO_PLID: @plid filled
* @STATION_INFO_PLINK_STATE: @plink_state filled
*/ */
enum station_stats_flags { enum station_info_flags {
STATION_STAT_INACTIVE_TIME = 1<<0, STATION_INFO_INACTIVE_TIME = 1<<0,
STATION_STAT_RX_BYTES = 1<<1, STATION_INFO_RX_BYTES = 1<<1,
STATION_STAT_TX_BYTES = 1<<2, STATION_INFO_TX_BYTES = 1<<2,
STATION_INFO_LLID = 1<<3,
STATION_INFO_PLID = 1<<4,
STATION_INFO_PLINK_STATE = 1<<5,
}; };
/** /**
* struct station_stats - station statistics * struct station_info - station information
* *
* Station information filled by driver for get_station(). * Station information filled by driver for get_station() and dump_station.
* *
* @filled: bitflag of flags from &enum station_stats_flags * @filled: bitflag of flags from &enum station_info_flags
* @inactive_time: time since last station activity (tx/rx) in milliseconds * @inactive_time: time since last station activity (tx/rx) in milliseconds
* @rx_bytes: bytes received from this station * @rx_bytes: bytes received from this station
* @tx_bytes: bytes transmitted to this station * @tx_bytes: bytes transmitted to this station
* @llid: mesh local link id
* @plid: mesh peer link id
* @plink_state: mesh peer link state
*/ */
struct station_stats { struct station_info {
u32 filled; u32 filled;
u32 inactive_time; u32 inactive_time;
u32 rx_bytes; u32 rx_bytes;
u32 tx_bytes; u32 tx_bytes;
u16 llid;
u16 plid;
u8 plink_state;
}; };
/** /**
...@@ -183,6 +219,56 @@ enum monitor_flags { ...@@ -183,6 +219,56 @@ enum monitor_flags {
MONITOR_FLAG_COOK_FRAMES = 1<<NL80211_MNTR_FLAG_COOK_FRAMES, MONITOR_FLAG_COOK_FRAMES = 1<<NL80211_MNTR_FLAG_COOK_FRAMES,
}; };
/**
* enum mpath_info_flags - mesh path information flags
*
* Used by the driver to indicate which info in &struct mpath_info it has filled
* in during get_station() or dump_station().
*
* MPATH_INFO_FRAME_QLEN: @frame_qlen filled
* MPATH_INFO_DSN: @dsn filled
* MPATH_INFO_METRIC: @metric filled
* MPATH_INFO_EXPTIME: @exptime filled
* MPATH_INFO_DISCOVERY_TIMEOUT: @discovery_timeout filled
* MPATH_INFO_DISCOVERY_RETRIES: @discovery_retries filled
* MPATH_INFO_FLAGS: @flags filled
*/
enum mpath_info_flags {
MPATH_INFO_FRAME_QLEN = BIT(0),
MPATH_INFO_DSN = BIT(1),
MPATH_INFO_METRIC = BIT(2),
MPATH_INFO_EXPTIME = BIT(3),
MPATH_INFO_DISCOVERY_TIMEOUT = BIT(4),
MPATH_INFO_DISCOVERY_RETRIES = BIT(5),
MPATH_INFO_FLAGS = BIT(6),
};
/**
* struct mpath_info - mesh path information
*
* Mesh path information filled by driver for get_mpath() and dump_mpath().
*
* @filled: bitfield of flags from &enum mpath_info_flags
* @frame_qlen: number of queued frames for this destination
* @dsn: destination sequence number
* @metric: metric (cost) of this mesh path
* @exptime: expiration time for the mesh path from now, in msecs
* @flags: mesh path flags
* @discovery_timeout: total mesh path discovery timeout, in msecs
* @discovery_retries: mesh path discovery retries
*/
struct mpath_info {
u32 filled;
u32 frame_qlen;
u32 dsn;
u32 metric;
u32 exptime;
u32 discovery_timeout;
u8 discovery_retries;
u8 flags;
};
/* from net/wireless.h */ /* from net/wireless.h */
struct wiphy; struct wiphy;
...@@ -230,13 +316,17 @@ struct wiphy; ...@@ -230,13 +316,17 @@ struct wiphy;
* @del_station: Remove a station; @mac may be NULL to remove all stations. * @del_station: Remove a station; @mac may be NULL to remove all stations.
* *
* @change_station: Modify a given station. * @change_station: Modify a given station.
*
* @set_mesh_cfg: set mesh parameters (by now, just mesh id)
*/ */
struct cfg80211_ops { struct cfg80211_ops {
int (*add_virtual_intf)(struct wiphy *wiphy, char *name, int (*add_virtual_intf)(struct wiphy *wiphy, char *name,
enum nl80211_iftype type, u32 *flags); enum nl80211_iftype type, u32 *flags,
struct vif_params *params);
int (*del_virtual_intf)(struct wiphy *wiphy, int ifindex); int (*del_virtual_intf)(struct wiphy *wiphy, int ifindex);
int (*change_virtual_intf)(struct wiphy *wiphy, int ifindex, int (*change_virtual_intf)(struct wiphy *wiphy, int ifindex,
enum nl80211_iftype type, u32 *flags); enum nl80211_iftype type, u32 *flags,
struct vif_params *params);
int (*add_key)(struct wiphy *wiphy, struct net_device *netdev, int (*add_key)(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, u8 *mac_addr, u8 key_index, u8 *mac_addr,
...@@ -264,7 +354,22 @@ struct cfg80211_ops { ...@@ -264,7 +354,22 @@ struct cfg80211_ops {
int (*change_station)(struct wiphy *wiphy, struct net_device *dev, int (*change_station)(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_parameters *params); u8 *mac, struct station_parameters *params);
int (*get_station)(struct wiphy *wiphy, struct net_device *dev, int (*get_station)(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_stats *stats); u8 *mac, struct station_info *sinfo);
int (*dump_station)(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *mac, struct station_info *sinfo);
int (*add_mpath)(struct wiphy *wiphy, struct net_device *dev,
u8 *dst, u8 *next_hop);
int (*del_mpath)(struct wiphy *wiphy, struct net_device *dev,
u8 *dst);
int (*change_mpath)(struct wiphy *wiphy, struct net_device *dev,
u8 *dst, u8 *next_hop);
int (*get_mpath)(struct wiphy *wiphy, struct net_device *dev,
u8 *dst, u8 *next_hop,
struct mpath_info *pinfo);
int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *dst, u8 *next_hop,
struct mpath_info *pinfo);
}; };
#endif /* __NET_CFG80211_H */ #endif /* __NET_CFG80211_H */
...@@ -34,7 +34,8 @@ nl80211_type_to_mac80211_type(enum nl80211_iftype type) ...@@ -34,7 +34,8 @@ nl80211_type_to_mac80211_type(enum nl80211_iftype type)
} }
static int ieee80211_add_iface(struct wiphy *wiphy, char *name, static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
enum nl80211_iftype type, u32 *flags) enum nl80211_iftype type, u32 *flags,
struct vif_params *params)
{ {
struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_local *local = wiphy_priv(wiphy);
enum ieee80211_if_types itype; enum ieee80211_if_types itype;
...@@ -78,7 +79,8 @@ static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex) ...@@ -78,7 +79,8 @@ static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex)
} }
static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex, static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
enum nl80211_iftype type, u32 *flags) enum nl80211_iftype type, u32 *flags,
struct vif_params *params)
{ {
struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_local *local = wiphy_priv(wiphy);
struct net_device *dev; struct net_device *dev;
...@@ -296,7 +298,7 @@ static int ieee80211_config_default_key(struct wiphy *wiphy, ...@@ -296,7 +298,7 @@ static int ieee80211_config_default_key(struct wiphy *wiphy,
} }
static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_stats *stats) u8 *mac, struct station_info *sinfo)
{ {
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta; struct sta_info *sta;
...@@ -307,13 +309,13 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, ...@@ -307,13 +309,13 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
/* XXX: verify sta->dev == dev */ /* XXX: verify sta->dev == dev */
stats->filled = STATION_STAT_INACTIVE_TIME | sinfo->filled = STATION_INFO_INACTIVE_TIME |
STATION_STAT_RX_BYTES | STATION_INFO_RX_BYTES |
STATION_STAT_TX_BYTES; STATION_INFO_TX_BYTES;
stats->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
stats->rx_bytes = sta->rx_bytes; sinfo->rx_bytes = sta->rx_bytes;
stats->tx_bytes = sta->tx_bytes; sinfo->tx_bytes = sta->tx_bytes;
sta_info_put(sta); sta_info_put(sta);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment