Commit 107bc0aa authored by David S. Miller's avatar David S. Miller

Merge branch 'for-upstream' of...

Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next

Johan Hedberg says:

====================
pull request: bluetooth-next 2016-12-08

I didn't miss your "net-next is closed" email, but it did come as a bit
of a surprise, and due to time-zone differences I didn't have a chance
to react to it until now. We would have had a couple of patches in
bluetooth-next that we'd still have wanted to get to 4.10.

Out of these the most critical one is the H7/CT2 patch for Bluetooth
Security Manager Protocol, something that couldn't be published before
the Bluetooth 5.0 specification went public (yesterday). If these really
can't go to net-next we'll likely be sending at least this patch through
bluetooth.git to net.git for rc1 inclusion.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ea1e76f7 a62da6f1
...@@ -32,7 +32,6 @@ ...@@ -32,7 +32,6 @@
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#define BTM_HEADER_LEN 4 #define BTM_HEADER_LEN 4
......
...@@ -58,6 +58,11 @@ struct atusb { ...@@ -58,6 +58,11 @@ struct atusb {
struct urb *tx_urb; struct urb *tx_urb;
struct sk_buff *tx_skb; struct sk_buff *tx_skb;
uint8_t tx_ack_seq; /* current TX ACK sequence number */ uint8_t tx_ack_seq; /* current TX ACK sequence number */
/* Firmware variable */
unsigned char fw_ver_maj; /* Firmware major version number */
unsigned char fw_ver_min; /* Firmware minor version number */
unsigned char fw_hw_type; /* Firmware hardware type */
}; };
/* ----- USB commands without data ----------------------------------------- */ /* ----- USB commands without data ----------------------------------------- */
...@@ -540,6 +545,21 @@ atusb_set_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be, u8 retries ...@@ -540,6 +545,21 @@ atusb_set_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be, u8 retries
return atusb_write_subreg(atusb, SR_MAX_CSMA_RETRIES, retries); return atusb_write_subreg(atusb, SR_MAX_CSMA_RETRIES, retries);
} }
static int
atusb_set_frame_retries(struct ieee802154_hw *hw, s8 retries)
{
struct atusb *atusb = hw->priv;
struct device *dev = &atusb->usb_dev->dev;
if (atusb->fw_ver_maj == 0 && atusb->fw_ver_min < 3) {
dev_info(dev, "Automatic frame retransmission is only available from "
"firmware version 0.3. Please update if you want this feature.");
return -EINVAL;
}
return atusb_write_subreg(atusb, SR_MAX_FRAME_RETRIES, retries);
}
static int static int
atusb_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) atusb_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on)
{ {
...@@ -579,6 +599,7 @@ static const struct ieee802154_ops atusb_ops = { ...@@ -579,6 +599,7 @@ static const struct ieee802154_ops atusb_ops = {
.set_cca_mode = atusb_set_cca_mode, .set_cca_mode = atusb_set_cca_mode,
.set_cca_ed_level = atusb_set_cca_ed_level, .set_cca_ed_level = atusb_set_cca_ed_level,
.set_csma_params = atusb_set_csma_params, .set_csma_params = atusb_set_csma_params,
.set_frame_retries = atusb_set_frame_retries,
.set_promiscuous_mode = atusb_set_promiscuous_mode, .set_promiscuous_mode = atusb_set_promiscuous_mode,
}; };
...@@ -594,14 +615,19 @@ static int atusb_get_and_show_revision(struct atusb *atusb) ...@@ -594,14 +615,19 @@ static int atusb_get_and_show_revision(struct atusb *atusb)
ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0), ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0),
ATUSB_ID, ATUSB_REQ_FROM_DEV, 0, 0, ATUSB_ID, ATUSB_REQ_FROM_DEV, 0, 0,
buffer, 3, 1000); buffer, 3, 1000);
if (ret >= 0) if (ret >= 0) {
atusb->fw_ver_maj = buffer[0];
atusb->fw_ver_min = buffer[1];
atusb->fw_hw_type = buffer[2];
dev_info(&usb_dev->dev, dev_info(&usb_dev->dev,
"Firmware: major: %u, minor: %u, hardware type: %u\n", "Firmware: major: %u, minor: %u, hardware type: %u\n",
buffer[0], buffer[1], buffer[2]); atusb->fw_ver_maj, atusb->fw_ver_min, atusb->fw_hw_type);
if (buffer[0] == 0 && buffer[1] < 2) { }
if (atusb->fw_ver_maj == 0 && atusb->fw_ver_min < 2) {
dev_info(&usb_dev->dev, dev_info(&usb_dev->dev,
"Firmware version (%u.%u) is predates our first public release.", "Firmware version (%u.%u) predates our first public release.",
buffer[0], buffer[1]); atusb->fw_ver_maj, atusb->fw_ver_min);
dev_info(&usb_dev->dev, "Please update to version 0.2 or newer"); dev_info(&usb_dev->dev, "Please update to version 0.2 or newer");
} }
...@@ -669,6 +695,43 @@ static int atusb_get_and_show_chip(struct atusb *atusb) ...@@ -669,6 +695,43 @@ static int atusb_get_and_show_chip(struct atusb *atusb)
return -ENODEV; return -ENODEV;
} }
static int atusb_set_extended_addr(struct atusb *atusb)
{
struct usb_device *usb_dev = atusb->usb_dev;
unsigned char buffer[IEEE802154_EXTENDED_ADDR_LEN];
__le64 extended_addr;
u64 addr;
int ret;
/* Firmware versions before 0.3 do not support the EUI64_READ command.
* Just use a random address and be done */
if (atusb->fw_ver_maj == 0 && atusb->fw_ver_min < 3) {
ieee802154_random_extended_addr(&atusb->hw->phy->perm_extended_addr);
return 0;
}
/* Firmware is new enough so we fetch the address from EEPROM */
ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0),
ATUSB_EUI64_READ, ATUSB_REQ_FROM_DEV, 0, 0,
buffer, IEEE802154_EXTENDED_ADDR_LEN, 1000);
if (ret < 0)
dev_err(&usb_dev->dev, "failed to fetch extended address\n");
memcpy(&extended_addr, buffer, IEEE802154_EXTENDED_ADDR_LEN);
/* Check if read address is not empty and the unicast bit is set correctly */
if (!ieee802154_is_valid_extended_unicast_addr(extended_addr)) {
dev_info(&usb_dev->dev, "no permanent extended address found, random address set\n");
ieee802154_random_extended_addr(&atusb->hw->phy->perm_extended_addr);
} else {
atusb->hw->phy->perm_extended_addr = extended_addr;
addr = swab64((__force u64)atusb->hw->phy->perm_extended_addr);
dev_info(&usb_dev->dev, "Read permanent extended address %8phC from device\n",
&addr);
}
return ret;
}
/* ----- Setup ------------------------------------------------------------- */ /* ----- Setup ------------------------------------------------------------- */
static int atusb_probe(struct usb_interface *interface, static int atusb_probe(struct usb_interface *interface,
...@@ -707,7 +770,8 @@ static int atusb_probe(struct usb_interface *interface, ...@@ -707,7 +770,8 @@ static int atusb_probe(struct usb_interface *interface,
hw->parent = &usb_dev->dev; hw->parent = &usb_dev->dev;
hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT | hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT |
IEEE802154_HW_PROMISCUOUS | IEEE802154_HW_CSMA_PARAMS; IEEE802154_HW_PROMISCUOUS | IEEE802154_HW_CSMA_PARAMS |
IEEE802154_HW_FRAME_RETRIES;
hw->phy->flags = WPAN_PHY_FLAG_TXPOWER | WPAN_PHY_FLAG_CCA_ED_LEVEL | hw->phy->flags = WPAN_PHY_FLAG_TXPOWER | WPAN_PHY_FLAG_CCA_ED_LEVEL |
WPAN_PHY_FLAG_CCA_MODE; WPAN_PHY_FLAG_CCA_MODE;
...@@ -728,13 +792,14 @@ static int atusb_probe(struct usb_interface *interface, ...@@ -728,13 +792,14 @@ static int atusb_probe(struct usb_interface *interface,
hw->phy->supported.tx_powers = atusb_powers; hw->phy->supported.tx_powers = atusb_powers;
hw->phy->supported.tx_powers_size = ARRAY_SIZE(atusb_powers); hw->phy->supported.tx_powers_size = ARRAY_SIZE(atusb_powers);
hw->phy->transmit_power = hw->phy->supported.tx_powers[0]; hw->phy->transmit_power = hw->phy->supported.tx_powers[0];
ieee802154_random_extended_addr(&hw->phy->perm_extended_addr);
hw->phy->cca_ed_level = hw->phy->supported.cca_ed_levels[7]; hw->phy->cca_ed_level = hw->phy->supported.cca_ed_levels[7];
atusb_command(atusb, ATUSB_RF_RESET, 0); atusb_command(atusb, ATUSB_RF_RESET, 0);
atusb_get_and_show_chip(atusb); atusb_get_and_show_chip(atusb);
atusb_get_and_show_revision(atusb); atusb_get_and_show_revision(atusb);
atusb_get_and_show_build(atusb); atusb_get_and_show_build(atusb);
atusb_set_extended_addr(atusb);
ret = atusb_get_and_clear_error(atusb); ret = atusb_get_and_clear_error(atusb);
if (ret) { if (ret) {
dev_err(&atusb->usb_dev->dev, dev_err(&atusb->usb_dev->dev,
......
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
* Firmware: ben-wpan/atusb/fw/include/atusb/atusb.h * Firmware: ben-wpan/atusb/fw/include/atusb/atusb.h
*/ */
#ifndef _ATUSB_H #ifndef _ATUSB_H
#define _ATUSB_H #define _ATUSB_H
#define ATUSB_VENDOR_ID 0x20b7 /* Qi Hardware*/ #define ATUSB_VENDOR_ID 0x20b7 /* Qi Hardware*/
#define ATUSB_PRODUCT_ID 0x1540 /* 802.15.4, device 0 */ #define ATUSB_PRODUCT_ID 0x1540 /* 802.15.4, device 0 */
...@@ -46,9 +46,12 @@ enum atusb_requests { ...@@ -46,9 +46,12 @@ enum atusb_requests {
ATUSB_SPI_WRITE2_SYNC, ATUSB_SPI_WRITE2_SYNC,
ATUSB_RX_MODE = 0x40, /* HardMAC group */ ATUSB_RX_MODE = 0x40, /* HardMAC group */
ATUSB_TX, ATUSB_TX,
ATUSB_EUI64_WRITE = 0x50, /* Parameter in EEPROM grp */
ATUSB_EUI64_READ,
}; };
/* Direction bRequest wValue wIndex wLength /*
* Direction bRequest wValue wIndex wLength
* *
* ->host ATUSB_ID - - 3 * ->host ATUSB_ID - - 3
* ->host ATUSB_BUILD - - #bytes * ->host ATUSB_BUILD - - #bytes
...@@ -76,6 +79,8 @@ enum atusb_requests { ...@@ -76,6 +79,8 @@ enum atusb_requests {
* *
* host-> ATUSB_RX_MODE on - 0 * host-> ATUSB_RX_MODE on - 0
* host-> ATUSB_TX flags ack_seq #bytes * host-> ATUSB_TX flags ack_seq #bytes
* host-> ATUSB_EUI64_WRITE - - #bytes (8)
* ->host ATUSB_EUI64_READ - - #bytes (8)
*/ */
#define ATUSB_REQ_FROM_DEV (USB_TYPE_VENDOR | USB_DIR_IN) #define ATUSB_REQ_FROM_DEV (USB_TYPE_VENDOR | USB_DIR_IN)
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
#define SMP_TIMEOUT msecs_to_jiffies(30000) #define SMP_TIMEOUT msecs_to_jiffies(30000)
#define AUTH_REQ_MASK(dev) (hci_dev_test_flag(dev, HCI_SC_ENABLED) ? \ #define AUTH_REQ_MASK(dev) (hci_dev_test_flag(dev, HCI_SC_ENABLED) ? \
0x1f : 0x07) 0x3f : 0x07)
#define KEY_DIST_MASK 0x07 #define KEY_DIST_MASK 0x07
/* Maximum message length that can be passed to aes_cmac */ /* Maximum message length that can be passed to aes_cmac */
...@@ -76,6 +76,7 @@ enum { ...@@ -76,6 +76,7 @@ enum {
SMP_FLAG_DHKEY_PENDING, SMP_FLAG_DHKEY_PENDING,
SMP_FLAG_REMOTE_OOB, SMP_FLAG_REMOTE_OOB,
SMP_FLAG_LOCAL_OOB, SMP_FLAG_LOCAL_OOB,
SMP_FLAG_CT2,
}; };
struct smp_dev { struct smp_dev {
...@@ -357,6 +358,22 @@ static int smp_h6(struct crypto_shash *tfm_cmac, const u8 w[16], ...@@ -357,6 +358,22 @@ static int smp_h6(struct crypto_shash *tfm_cmac, const u8 w[16],
return err; return err;
} }
static int smp_h7(struct crypto_shash *tfm_cmac, const u8 w[16],
const u8 salt[16], u8 res[16])
{
int err;
SMP_DBG("w %16phN salt %16phN", w, salt);
err = aes_cmac(tfm_cmac, salt, w, 16, res);
if (err)
return err;
SMP_DBG("res %16phN", res);
return err;
}
/* The following functions map to the legacy SMP crypto functions e, c1, /* The following functions map to the legacy SMP crypto functions e, c1,
* s1 and ah. * s1 and ah.
*/ */
...@@ -1130,20 +1147,31 @@ static void sc_add_ltk(struct smp_chan *smp) ...@@ -1130,20 +1147,31 @@ static void sc_add_ltk(struct smp_chan *smp)
static void sc_generate_link_key(struct smp_chan *smp) static void sc_generate_link_key(struct smp_chan *smp)
{ {
/* These constants are as specified in the core specification. /* From core spec. Spells out in ASCII as 'lebr'. */
* In ASCII they spell out to 'tmp1' and 'lebr'.
*/
const u8 tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 };
const u8 lebr[4] = { 0x72, 0x62, 0x65, 0x6c }; const u8 lebr[4] = { 0x72, 0x62, 0x65, 0x6c };
smp->link_key = kzalloc(16, GFP_KERNEL); smp->link_key = kzalloc(16, GFP_KERNEL);
if (!smp->link_key) if (!smp->link_key)
return; return;
if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) { if (test_bit(SMP_FLAG_CT2, &smp->flags)) {
kzfree(smp->link_key); /* SALT = 0x00000000000000000000000000000000746D7031 */
smp->link_key = NULL; const u8 salt[16] = { 0x31, 0x70, 0x6d, 0x74 };
return;
if (smp_h7(smp->tfm_cmac, smp->tk, salt, smp->link_key)) {
kzfree(smp->link_key);
smp->link_key = NULL;
return;
}
} else {
/* From core spec. Spells out in ASCII as 'tmp1'. */
const u8 tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 };
if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) {
kzfree(smp->link_key);
smp->link_key = NULL;
return;
}
} }
if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) { if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) {
...@@ -1169,10 +1197,7 @@ static void smp_allow_key_dist(struct smp_chan *smp) ...@@ -1169,10 +1197,7 @@ static void smp_allow_key_dist(struct smp_chan *smp)
static void sc_generate_ltk(struct smp_chan *smp) static void sc_generate_ltk(struct smp_chan *smp)
{ {
/* These constants are as specified in the core specification. /* From core spec. Spells out in ASCII as 'brle'. */
* In ASCII they spell out to 'tmp2' and 'brle'.
*/
const u8 tmp2[4] = { 0x32, 0x70, 0x6d, 0x74 };
const u8 brle[4] = { 0x65, 0x6c, 0x72, 0x62 }; const u8 brle[4] = { 0x65, 0x6c, 0x72, 0x62 };
struct hci_conn *hcon = smp->conn->hcon; struct hci_conn *hcon = smp->conn->hcon;
struct hci_dev *hdev = hcon->hdev; struct hci_dev *hdev = hcon->hdev;
...@@ -1187,8 +1212,19 @@ static void sc_generate_ltk(struct smp_chan *smp) ...@@ -1187,8 +1212,19 @@ static void sc_generate_ltk(struct smp_chan *smp)
if (key->type == HCI_LK_DEBUG_COMBINATION) if (key->type == HCI_LK_DEBUG_COMBINATION)
set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags); set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
if (smp_h6(smp->tfm_cmac, key->val, tmp2, smp->tk)) if (test_bit(SMP_FLAG_CT2, &smp->flags)) {
return; /* SALT = 0x00000000000000000000000000000000746D7032 */
const u8 salt[16] = { 0x32, 0x70, 0x6d, 0x74 };
if (smp_h7(smp->tfm_cmac, key->val, salt, smp->tk))
return;
} else {
/* From core spec. Spells out in ASCII as 'tmp2'. */
const u8 tmp2[4] = { 0x32, 0x70, 0x6d, 0x74 };
if (smp_h6(smp->tfm_cmac, key->val, tmp2, smp->tk))
return;
}
if (smp_h6(smp->tfm_cmac, smp->tk, brle, smp->tk)) if (smp_h6(smp->tfm_cmac, smp->tk, brle, smp->tk))
return; return;
...@@ -1669,6 +1705,7 @@ static void build_bredr_pairing_cmd(struct smp_chan *smp, ...@@ -1669,6 +1705,7 @@ static void build_bredr_pairing_cmd(struct smp_chan *smp,
if (!rsp) { if (!rsp) {
memset(req, 0, sizeof(*req)); memset(req, 0, sizeof(*req));
req->auth_req = SMP_AUTH_CT2;
req->init_key_dist = local_dist; req->init_key_dist = local_dist;
req->resp_key_dist = remote_dist; req->resp_key_dist = remote_dist;
req->max_key_size = conn->hcon->enc_key_size; req->max_key_size = conn->hcon->enc_key_size;
...@@ -1680,6 +1717,7 @@ static void build_bredr_pairing_cmd(struct smp_chan *smp, ...@@ -1680,6 +1717,7 @@ static void build_bredr_pairing_cmd(struct smp_chan *smp,
memset(rsp, 0, sizeof(*rsp)); memset(rsp, 0, sizeof(*rsp));
rsp->auth_req = SMP_AUTH_CT2;
rsp->max_key_size = conn->hcon->enc_key_size; rsp->max_key_size = conn->hcon->enc_key_size;
rsp->init_key_dist = req->init_key_dist & remote_dist; rsp->init_key_dist = req->init_key_dist & remote_dist;
rsp->resp_key_dist = req->resp_key_dist & local_dist; rsp->resp_key_dist = req->resp_key_dist & local_dist;
...@@ -1744,6 +1782,9 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) ...@@ -1744,6 +1782,9 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
build_bredr_pairing_cmd(smp, req, &rsp); build_bredr_pairing_cmd(smp, req, &rsp);
if (req->auth_req & SMP_AUTH_CT2)
set_bit(SMP_FLAG_CT2, &smp->flags);
key_size = min(req->max_key_size, rsp.max_key_size); key_size = min(req->max_key_size, rsp.max_key_size);
if (check_enc_key_size(conn, key_size)) if (check_enc_key_size(conn, key_size))
return SMP_ENC_KEY_SIZE; return SMP_ENC_KEY_SIZE;
...@@ -1761,9 +1802,13 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) ...@@ -1761,9 +1802,13 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
build_pairing_cmd(conn, req, &rsp, auth); build_pairing_cmd(conn, req, &rsp, auth);
if (rsp.auth_req & SMP_AUTH_SC) if (rsp.auth_req & SMP_AUTH_SC) {
set_bit(SMP_FLAG_SC, &smp->flags); set_bit(SMP_FLAG_SC, &smp->flags);
if (rsp.auth_req & SMP_AUTH_CT2)
set_bit(SMP_FLAG_CT2, &smp->flags);
}
if (conn->hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT) if (conn->hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
sec_level = BT_SECURITY_MEDIUM; sec_level = BT_SECURITY_MEDIUM;
else else
...@@ -1917,6 +1962,9 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) ...@@ -1917,6 +1962,9 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
*/ */
smp->remote_key_dist &= rsp->resp_key_dist; smp->remote_key_dist &= rsp->resp_key_dist;
if ((req->auth_req & SMP_AUTH_CT2) && (auth & SMP_AUTH_CT2))
set_bit(SMP_FLAG_CT2, &smp->flags);
/* For BR/EDR this means we're done and can start phase 3 */ /* For BR/EDR this means we're done and can start phase 3 */
if (conn->hcon->type == ACL_LINK) { if (conn->hcon->type == ACL_LINK) {
/* Clear bits which are generated but not distributed */ /* Clear bits which are generated but not distributed */
...@@ -2312,8 +2360,11 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) ...@@ -2312,8 +2360,11 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
authreq = seclevel_to_authreq(sec_level); authreq = seclevel_to_authreq(sec_level);
if (hci_dev_test_flag(hcon->hdev, HCI_SC_ENABLED)) if (hci_dev_test_flag(hcon->hdev, HCI_SC_ENABLED)) {
authreq |= SMP_AUTH_SC; authreq |= SMP_AUTH_SC;
if (hci_dev_test_flag(hcon->hdev, HCI_SSP_ENABLED))
authreq |= SMP_AUTH_CT2;
}
/* Require MITM if IO Capability allows or the security level /* Require MITM if IO Capability allows or the security level
* requires it. * requires it.
......
...@@ -57,6 +57,7 @@ struct smp_cmd_pairing { ...@@ -57,6 +57,7 @@ struct smp_cmd_pairing {
#define SMP_AUTH_MITM 0x04 #define SMP_AUTH_MITM 0x04
#define SMP_AUTH_SC 0x08 #define SMP_AUTH_SC 0x08
#define SMP_AUTH_KEYPRESS 0x10 #define SMP_AUTH_KEYPRESS 0x10
#define SMP_AUTH_CT2 0x20
#define SMP_CMD_PAIRING_CONFIRM 0x03 #define SMP_CMD_PAIRING_CONFIRM 0x03
struct smp_cmd_pairing_confirm { struct smp_cmd_pairing_confirm {
......
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