mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/
synced 2026-04-01 22:37:41 -04:00
Compare commits
28 Commits
48b3cd6926
...
aba53ccf05
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aba53ccf05 | ||
|
|
6d6be7070e | ||
|
|
a54ecccfae | ||
|
|
ffb5a4843c | ||
|
|
bc39a09473 | ||
|
|
20756fec2f | ||
|
|
d05111bfe3 | ||
|
|
bda93eec78 | ||
|
|
b255531b27 | ||
|
|
a2639a7f0f | ||
|
|
b8dbe9648d | ||
|
|
0ffac654e9 | ||
|
|
035c25007c | ||
|
|
aca377208e | ||
|
|
2969554bcf | ||
|
|
2b2bf47cd7 | ||
|
|
8a5b0135d4 | ||
|
|
a834a0b66e | ||
|
|
da107398cb | ||
|
|
3d5d488f11 | ||
|
|
9862ef9ab0 | ||
|
|
917b61fa20 | ||
|
|
35177c6877 | ||
|
|
a242a9ae58 | ||
|
|
b7e8590987 | ||
|
|
a958a4f90d | ||
|
|
6d52a4a052 | ||
|
|
76522fcdbc |
@@ -109,9 +109,6 @@ static int h4_recv(struct hci_uart *hu, const void *data, int count)
|
||||
{
|
||||
struct h4_struct *h4 = hu->priv;
|
||||
|
||||
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
|
||||
return -EUNATCH;
|
||||
|
||||
h4->rx_skb = h4_recv_buf(hu, h4->rx_skb, data, count,
|
||||
h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts));
|
||||
if (IS_ERR(h4->rx_skb)) {
|
||||
|
||||
@@ -309,7 +309,7 @@ enum {
|
||||
|
||||
/* register and unregister set references */
|
||||
extern ip_set_id_t ip_set_get_byname(struct net *net,
|
||||
const char *name, struct ip_set **set);
|
||||
const struct nlattr *name, struct ip_set **set);
|
||||
extern void ip_set_put_byindex(struct net *net, ip_set_id_t index);
|
||||
extern void ip_set_name_byindex(struct net *net, ip_set_id_t index, char *name);
|
||||
extern ip_set_id_t ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index);
|
||||
|
||||
@@ -1843,9 +1843,13 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data)
|
||||
u8 aux_num_cis = 0;
|
||||
u8 cis_id;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_cig(hdev, cig_id);
|
||||
if (!conn)
|
||||
if (!conn) {
|
||||
hci_dev_unlock(hdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
qos = &conn->iso_qos;
|
||||
pdu->cig_id = cig_id;
|
||||
@@ -1884,6 +1888,8 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data)
|
||||
}
|
||||
pdu->num_cis = aux_num_cis;
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
if (!pdu->num_cis)
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -80,6 +80,10 @@ static void *hci_le_ev_skb_pull(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
return data;
|
||||
}
|
||||
|
||||
static void hci_store_wake_reason(struct hci_dev *hdev,
|
||||
const bdaddr_t *bdaddr, u8 addr_type)
|
||||
__must_hold(&hdev->lock);
|
||||
|
||||
static u8 hci_cc_inquiry_cancel(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
@@ -3111,6 +3115,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR);
|
||||
|
||||
/* Check for existing connection:
|
||||
*
|
||||
@@ -3274,6 +3279,10 @@ static void hci_conn_request_evt(struct hci_dev *hdev, void *data,
|
||||
|
||||
bt_dev_dbg(hdev, "bdaddr %pMR type 0x%x", &ev->bdaddr, ev->link_type);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR);
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
/* Reject incoming connection from device with same BD ADDR against
|
||||
* CVE-2020-26555
|
||||
*/
|
||||
@@ -5021,6 +5030,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR);
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr);
|
||||
if (!conn) {
|
||||
@@ -5713,6 +5723,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
||||
int err;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_store_wake_reason(hdev, bdaddr, bdaddr_type);
|
||||
|
||||
/* All controllers implicitly stop advertising in the event of a
|
||||
* connection, so ensure that the state bit is cleared.
|
||||
@@ -6005,6 +6016,7 @@ static void hci_le_past_received_evt(struct hci_dev *hdev, void *data,
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_store_wake_reason(hdev, &ev->bdaddr, ev->bdaddr_type);
|
||||
|
||||
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
|
||||
|
||||
@@ -6403,6 +6415,8 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
info->length + 1))
|
||||
break;
|
||||
|
||||
hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type);
|
||||
|
||||
if (info->length <= max_adv_len(hdev)) {
|
||||
rssi = info->data[info->length];
|
||||
process_adv_report(hdev, info->type, &info->bdaddr,
|
||||
@@ -6491,6 +6505,8 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
info->length))
|
||||
break;
|
||||
|
||||
hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type);
|
||||
|
||||
evt_type = __le16_to_cpu(info->type) & LE_EXT_ADV_EVT_TYPE_MASK;
|
||||
legacy_evt_type = ext_evt_type_to_legacy(hdev, evt_type);
|
||||
|
||||
@@ -6536,6 +6552,7 @@ static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data,
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_store_wake_reason(hdev, &ev->bdaddr, ev->bdaddr_type);
|
||||
|
||||
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
|
||||
|
||||
@@ -6767,25 +6784,31 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data,
|
||||
latency = le16_to_cpu(ev->latency);
|
||||
timeout = le16_to_cpu(ev->timeout);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hcon = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!hcon || hcon->state != BT_CONNECTED)
|
||||
return send_conn_param_neg_reply(hdev, handle,
|
||||
HCI_ERROR_UNKNOWN_CONN_ID);
|
||||
if (!hcon || hcon->state != BT_CONNECTED) {
|
||||
send_conn_param_neg_reply(hdev, handle,
|
||||
HCI_ERROR_UNKNOWN_CONN_ID);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (max > hcon->le_conn_max_interval)
|
||||
return send_conn_param_neg_reply(hdev, handle,
|
||||
HCI_ERROR_INVALID_LL_PARAMS);
|
||||
if (max > hcon->le_conn_max_interval) {
|
||||
send_conn_param_neg_reply(hdev, handle,
|
||||
HCI_ERROR_INVALID_LL_PARAMS);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (hci_check_conn_params(min, max, latency, timeout))
|
||||
return send_conn_param_neg_reply(hdev, handle,
|
||||
HCI_ERROR_INVALID_LL_PARAMS);
|
||||
if (hci_check_conn_params(min, max, latency, timeout)) {
|
||||
send_conn_param_neg_reply(hdev, handle,
|
||||
HCI_ERROR_INVALID_LL_PARAMS);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (hcon->role == HCI_ROLE_MASTER) {
|
||||
struct hci_conn_params *params;
|
||||
u8 store_hint;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
params = hci_conn_params_lookup(hdev, &hcon->dst,
|
||||
hcon->dst_type);
|
||||
if (params) {
|
||||
@@ -6798,8 +6821,6 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data,
|
||||
store_hint = 0x00;
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
mgmt_new_conn_param(hdev, &hcon->dst, hcon->dst_type,
|
||||
store_hint, min, max, latency, timeout);
|
||||
}
|
||||
@@ -6813,6 +6834,9 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data,
|
||||
cp.max_ce_len = 0;
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_REPLY, sizeof(cp), &cp);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_le_direct_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
@@ -6834,6 +6858,8 @@ static void hci_le_direct_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
for (i = 0; i < ev->num; i++) {
|
||||
struct hci_ev_le_direct_adv_info *info = &ev->info[i];
|
||||
|
||||
hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type);
|
||||
|
||||
process_adv_report(hdev, info->type, &info->bdaddr,
|
||||
info->bdaddr_type, &info->direct_addr,
|
||||
info->direct_addr_type, HCI_ADV_PHY_1M, 0,
|
||||
@@ -7517,73 +7543,29 @@ static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hci_store_wake_reason(struct hci_dev *hdev, u8 event,
|
||||
struct sk_buff *skb)
|
||||
static void hci_store_wake_reason(struct hci_dev *hdev,
|
||||
const bdaddr_t *bdaddr, u8 addr_type)
|
||||
__must_hold(&hdev->lock)
|
||||
{
|
||||
struct hci_ev_le_advertising_info *adv;
|
||||
struct hci_ev_le_direct_adv_info *direct_adv;
|
||||
struct hci_ev_le_ext_adv_info *ext_adv;
|
||||
const struct hci_ev_conn_complete *conn_complete = (void *)skb->data;
|
||||
const struct hci_ev_conn_request *conn_request = (void *)skb->data;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
lockdep_assert_held(&hdev->lock);
|
||||
|
||||
/* If we are currently suspended and this is the first BT event seen,
|
||||
* save the wake reason associated with the event.
|
||||
*/
|
||||
if (!hdev->suspended || hdev->wake_reason)
|
||||
goto unlock;
|
||||
return;
|
||||
|
||||
if (!bdaddr) {
|
||||
hdev->wake_reason = MGMT_WAKE_REASON_UNEXPECTED;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Default to remote wake. Values for wake_reason are documented in the
|
||||
* Bluez mgmt api docs.
|
||||
*/
|
||||
hdev->wake_reason = MGMT_WAKE_REASON_REMOTE_WAKE;
|
||||
|
||||
/* Once configured for remote wakeup, we should only wake up for
|
||||
* reconnections. It's useful to see which device is waking us up so
|
||||
* keep track of the bdaddr of the connection event that woke us up.
|
||||
*/
|
||||
if (event == HCI_EV_CONN_REQUEST) {
|
||||
bacpy(&hdev->wake_addr, &conn_request->bdaddr);
|
||||
hdev->wake_addr_type = BDADDR_BREDR;
|
||||
} else if (event == HCI_EV_CONN_COMPLETE) {
|
||||
bacpy(&hdev->wake_addr, &conn_complete->bdaddr);
|
||||
hdev->wake_addr_type = BDADDR_BREDR;
|
||||
} else if (event == HCI_EV_LE_META) {
|
||||
struct hci_ev_le_meta *le_ev = (void *)skb->data;
|
||||
u8 subevent = le_ev->subevent;
|
||||
u8 *ptr = &skb->data[sizeof(*le_ev)];
|
||||
u8 num_reports = *ptr;
|
||||
|
||||
if ((subevent == HCI_EV_LE_ADVERTISING_REPORT ||
|
||||
subevent == HCI_EV_LE_DIRECT_ADV_REPORT ||
|
||||
subevent == HCI_EV_LE_EXT_ADV_REPORT) &&
|
||||
num_reports) {
|
||||
adv = (void *)(ptr + 1);
|
||||
direct_adv = (void *)(ptr + 1);
|
||||
ext_adv = (void *)(ptr + 1);
|
||||
|
||||
switch (subevent) {
|
||||
case HCI_EV_LE_ADVERTISING_REPORT:
|
||||
bacpy(&hdev->wake_addr, &adv->bdaddr);
|
||||
hdev->wake_addr_type = adv->bdaddr_type;
|
||||
break;
|
||||
case HCI_EV_LE_DIRECT_ADV_REPORT:
|
||||
bacpy(&hdev->wake_addr, &direct_adv->bdaddr);
|
||||
hdev->wake_addr_type = direct_adv->bdaddr_type;
|
||||
break;
|
||||
case HCI_EV_LE_EXT_ADV_REPORT:
|
||||
bacpy(&hdev->wake_addr, &ext_adv->bdaddr);
|
||||
hdev->wake_addr_type = ext_adv->bdaddr_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hdev->wake_reason = MGMT_WAKE_REASON_UNEXPECTED;
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
bacpy(&hdev->wake_addr, bdaddr);
|
||||
hdev->wake_addr_type = addr_type;
|
||||
}
|
||||
|
||||
#define HCI_EV_VL(_op, _func, _min_len, _max_len) \
|
||||
@@ -7830,14 +7812,15 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
skb_pull(skb, HCI_EVENT_HDR_SIZE);
|
||||
|
||||
/* Store wake reason if we're suspended */
|
||||
hci_store_wake_reason(hdev, event, skb);
|
||||
|
||||
bt_dev_dbg(hdev, "event 0x%2.2x", event);
|
||||
|
||||
hci_event_func(hdev, event, skb, &opcode, &status, &req_complete,
|
||||
&req_complete_skb);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_store_wake_reason(hdev, NULL, 0);
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
if (req_complete) {
|
||||
req_complete(hdev, status, opcode);
|
||||
} else if (req_complete_skb) {
|
||||
|
||||
@@ -780,7 +780,7 @@ int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy)
|
||||
{
|
||||
if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy))
|
||||
return 0;
|
||||
return -EEXIST;
|
||||
|
||||
return hci_cmd_sync_queue(hdev, func, data, destroy);
|
||||
}
|
||||
@@ -801,8 +801,15 @@ int hci_cmd_sync_run(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
return -ENETDOWN;
|
||||
|
||||
/* If on cmd_sync_work then run immediately otherwise queue */
|
||||
if (current_work() == &hdev->cmd_sync_work)
|
||||
return func(hdev, data);
|
||||
if (current_work() == &hdev->cmd_sync_work) {
|
||||
int err;
|
||||
|
||||
err = func(hdev, data);
|
||||
if (destroy)
|
||||
destroy(hdev, data, err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hci_cmd_sync_submit(hdev, func, data, destroy);
|
||||
}
|
||||
@@ -3255,6 +3262,8 @@ static int update_passive_scan_sync(struct hci_dev *hdev, void *data)
|
||||
|
||||
int hci_update_passive_scan(struct hci_dev *hdev)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Only queue if it would have any effect */
|
||||
if (!test_bit(HCI_UP, &hdev->flags) ||
|
||||
test_bit(HCI_INIT, &hdev->flags) ||
|
||||
@@ -3264,8 +3273,9 @@ int hci_update_passive_scan(struct hci_dev *hdev)
|
||||
hci_dev_test_flag(hdev, HCI_UNREGISTER))
|
||||
return 0;
|
||||
|
||||
return hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL,
|
||||
NULL);
|
||||
err = hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL,
|
||||
NULL);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
int hci_write_sc_support_sync(struct hci_dev *hdev, u8 val)
|
||||
@@ -6958,8 +6968,11 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
|
||||
|
||||
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
return hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
|
||||
NULL);
|
||||
int err;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
|
||||
NULL);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
|
||||
@@ -6995,8 +7008,11 @@ done:
|
||||
|
||||
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
return hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
|
||||
create_le_conn_complete);
|
||||
int err;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
|
||||
create_le_conn_complete);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
@@ -7203,8 +7219,11 @@ done:
|
||||
|
||||
int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
return hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
|
||||
create_pa_complete);
|
||||
int err;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
|
||||
create_pa_complete);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
static void create_big_complete(struct hci_dev *hdev, void *data, int err)
|
||||
@@ -7222,7 +7241,8 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err)
|
||||
|
||||
static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
DEFINE_FLEX(struct hci_cp_le_big_create_sync, cp, bis, num_bis, 0x11);
|
||||
DEFINE_FLEX(struct hci_cp_le_big_create_sync, cp, bis, num_bis,
|
||||
HCI_MAX_ISO_BIS);
|
||||
struct hci_conn *conn = data;
|
||||
struct bt_iso_qos *qos = &conn->iso_qos;
|
||||
int err;
|
||||
@@ -7266,8 +7286,11 @@ static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
|
||||
|
||||
int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
return hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
|
||||
create_big_complete);
|
||||
int err;
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
|
||||
create_big_complete);
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
struct past_data {
|
||||
@@ -7359,7 +7382,7 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
|
||||
if (err)
|
||||
kfree(data);
|
||||
|
||||
return err;
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
static void le_read_features_complete(struct hci_dev *hdev, void *data, int err)
|
||||
@@ -7368,10 +7391,8 @@ static void le_read_features_complete(struct hci_dev *hdev, void *data, int err)
|
||||
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
if (err == -ECANCELED)
|
||||
return;
|
||||
|
||||
hci_conn_drop(conn);
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
static int hci_le_read_all_remote_features_sync(struct hci_dev *hdev,
|
||||
@@ -7438,15 +7459,20 @@ int hci_le_read_remote_features(struct hci_conn *conn)
|
||||
* role is possible. Otherwise just transition into the
|
||||
* connected state without requesting the remote features.
|
||||
*/
|
||||
if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES))
|
||||
if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) {
|
||||
err = hci_cmd_sync_queue_once(hdev,
|
||||
hci_le_read_remote_features_sync,
|
||||
hci_conn_hold(conn),
|
||||
hci_conn_hold(hci_conn_get(conn)),
|
||||
le_read_features_complete);
|
||||
else
|
||||
if (err) {
|
||||
hci_conn_drop(conn);
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
} else {
|
||||
err = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return err;
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
static void pkt_type_changed(struct hci_dev *hdev, void *data, int err)
|
||||
@@ -7472,6 +7498,7 @@ int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_change_conn_ptype *cp;
|
||||
int err;
|
||||
|
||||
cp = kmalloc_obj(*cp);
|
||||
if (!cp)
|
||||
@@ -7480,8 +7507,12 @@ int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type)
|
||||
cp->handle = cpu_to_le16(conn->handle);
|
||||
cp->pkt_type = cpu_to_le16(pkt_type);
|
||||
|
||||
return hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp,
|
||||
pkt_type_changed);
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp,
|
||||
pkt_type_changed);
|
||||
if (err)
|
||||
kfree(cp);
|
||||
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
static void le_phy_update_complete(struct hci_dev *hdev, void *data, int err)
|
||||
@@ -7507,6 +7538,7 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_set_phy *cp;
|
||||
int err;
|
||||
|
||||
cp = kmalloc_obj(*cp);
|
||||
if (!cp)
|
||||
@@ -7517,6 +7549,10 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys)
|
||||
cp->tx_phys = tx_phys;
|
||||
cp->rx_phys = rx_phys;
|
||||
|
||||
return hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp,
|
||||
le_phy_update_complete);
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp,
|
||||
le_phy_update_complete);
|
||||
if (err)
|
||||
kfree(cp);
|
||||
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
@@ -2478,6 +2478,7 @@ static int mesh_send(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
struct mgmt_mesh_tx *mesh_tx;
|
||||
struct mgmt_cp_mesh_send *send = data;
|
||||
struct mgmt_rp_mesh_read_features rp;
|
||||
u16 expected_len;
|
||||
bool sending;
|
||||
int err = 0;
|
||||
|
||||
@@ -2485,12 +2486,19 @@ static int mesh_send(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
!hci_dev_test_flag(hdev, HCI_MESH_EXPERIMENTAL))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED) ||
|
||||
len <= MGMT_MESH_SEND_SIZE ||
|
||||
len > (MGMT_MESH_SEND_SIZE + 31))
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND,
|
||||
MGMT_STATUS_REJECTED);
|
||||
|
||||
if (!send->adv_data_len || send->adv_data_len > 31)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND,
|
||||
MGMT_STATUS_REJECTED);
|
||||
|
||||
expected_len = struct_size(send, adv_data, send->adv_data_len);
|
||||
if (expected_len != len)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
memset(&rp, 0, sizeof(rp));
|
||||
@@ -7248,6 +7256,9 @@ static bool ltk_is_valid(struct mgmt_ltk_info *key)
|
||||
if (key->initiator != 0x00 && key->initiator != 0x01)
|
||||
return false;
|
||||
|
||||
if (key->enc_size > sizeof(key->val))
|
||||
return false;
|
||||
|
||||
switch (key->addr.type) {
|
||||
case BDADDR_LE_PUBLIC:
|
||||
return true;
|
||||
|
||||
@@ -298,7 +298,7 @@ static int sco_chan_add(struct sco_conn *conn, struct sock *sk,
|
||||
int err = 0;
|
||||
|
||||
sco_conn_lock(conn);
|
||||
if (conn->sk)
|
||||
if (conn->sk || sco_pi(sk)->conn)
|
||||
err = -EBUSY;
|
||||
else
|
||||
__sco_chan_add(conn, sk, parent);
|
||||
@@ -353,9 +353,20 @@ static int sco_connect(struct sock *sk)
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
/* Recheck state after reacquiring the socket lock, as another
|
||||
* thread may have changed it (e.g., closed the socket).
|
||||
*/
|
||||
if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) {
|
||||
release_sock(sk);
|
||||
hci_conn_drop(hcon);
|
||||
err = -EBADFD;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = sco_chan_add(conn, sk, NULL);
|
||||
if (err) {
|
||||
release_sock(sk);
|
||||
hci_conn_drop(hcon);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
@@ -656,13 +667,18 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr_unsized *addr,
|
||||
addr->sa_family != AF_BLUETOOTH)
|
||||
return -EINVAL;
|
||||
|
||||
if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND)
|
||||
return -EBADFD;
|
||||
|
||||
if (sk->sk_type != SOCK_SEQPACKET)
|
||||
err = -EINVAL;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) {
|
||||
release_sock(sk);
|
||||
return -EBADFD;
|
||||
}
|
||||
|
||||
if (sk->sk_type != SOCK_SEQPACKET) {
|
||||
release_sock(sk);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set destination address and psm */
|
||||
bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr);
|
||||
release_sock(sk);
|
||||
|
||||
@@ -1018,10 +1018,7 @@ static u8 smp_random(struct smp_chan *smp)
|
||||
|
||||
smp_s1(smp->tk, smp->prnd, smp->rrnd, stk);
|
||||
|
||||
if (hcon->pending_sec_level == BT_SECURITY_HIGH)
|
||||
auth = 1;
|
||||
else
|
||||
auth = 0;
|
||||
auth = test_bit(SMP_FLAG_MITM_AUTH, &smp->flags) ? 1 : 0;
|
||||
|
||||
/* Even though there's no _RESPONDER suffix this is the
|
||||
* responder STK we're adding for later lookup (the initiator
|
||||
@@ -1826,7 +1823,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
if (sec_level > conn->hcon->pending_sec_level)
|
||||
conn->hcon->pending_sec_level = sec_level;
|
||||
|
||||
/* If we need MITM check that it can be achieved */
|
||||
/* If we need MITM check that it can be achieved. */
|
||||
if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) {
|
||||
u8 method;
|
||||
|
||||
@@ -1834,6 +1831,10 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
req->io_capability);
|
||||
if (method == JUST_WORKS || method == JUST_CFM)
|
||||
return SMP_AUTH_REQUIREMENTS;
|
||||
|
||||
/* Force MITM bit if it isn't set by the initiator. */
|
||||
auth |= SMP_AUTH_MITM;
|
||||
rsp.auth_req |= SMP_AUTH_MITM;
|
||||
}
|
||||
|
||||
key_size = min(req->max_key_size, rsp.max_key_size);
|
||||
|
||||
@@ -727,20 +727,28 @@ unlock:
|
||||
|
||||
void fib6_metric_set(struct fib6_info *f6i, int metric, u32 val)
|
||||
{
|
||||
struct dst_metrics *m;
|
||||
|
||||
if (!f6i)
|
||||
return;
|
||||
|
||||
if (f6i->fib6_metrics == &dst_default_metrics) {
|
||||
if (READ_ONCE(f6i->fib6_metrics) == &dst_default_metrics) {
|
||||
struct dst_metrics *dflt = (struct dst_metrics *)&dst_default_metrics;
|
||||
struct dst_metrics *p = kzalloc_obj(*p, GFP_ATOMIC);
|
||||
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
p->metrics[metric - 1] = val;
|
||||
refcount_set(&p->refcnt, 1);
|
||||
f6i->fib6_metrics = p;
|
||||
if (cmpxchg(&f6i->fib6_metrics, dflt, p) != dflt)
|
||||
kfree(p);
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
f6i->fib6_metrics->metrics[metric - 1] = val;
|
||||
m = READ_ONCE(f6i->fib6_metrics);
|
||||
WRITE_ONCE(m->metrics[metric - 1], val);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -821,7 +821,7 @@ EXPORT_SYMBOL_GPL(ip_set_del);
|
||||
*
|
||||
*/
|
||||
ip_set_id_t
|
||||
ip_set_get_byname(struct net *net, const char *name, struct ip_set **set)
|
||||
ip_set_get_byname(struct net *net, const struct nlattr *name, struct ip_set **set)
|
||||
{
|
||||
ip_set_id_t i, index = IPSET_INVALID_ID;
|
||||
struct ip_set *s;
|
||||
@@ -830,7 +830,7 @@ ip_set_get_byname(struct net *net, const char *name, struct ip_set **set)
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < inst->ip_set_max; i++) {
|
||||
s = rcu_dereference(inst->ip_set_list)[i];
|
||||
if (s && STRNCMP(s->name, name)) {
|
||||
if (s && nla_strcmp(name, s->name) == 0) {
|
||||
__ip_set_get(s);
|
||||
index = i;
|
||||
*set = s;
|
||||
|
||||
@@ -1098,7 +1098,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
|
||||
if (!test_bit(i, n->used))
|
||||
k++;
|
||||
}
|
||||
if (n->pos == 0 && k == 0) {
|
||||
if (k == n->pos) {
|
||||
t->hregion[r].ext_size -= ext_size(n->size, dsize);
|
||||
rcu_assign_pointer(hbucket(t, key), NULL);
|
||||
kfree_rcu(n, rcu);
|
||||
|
||||
@@ -367,7 +367,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
|
||||
ret = ip_set_get_extensions(set, tb, &ext);
|
||||
if (ret)
|
||||
return ret;
|
||||
e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s);
|
||||
e.id = ip_set_get_byname(map->net, tb[IPSET_ATTR_NAME], &s);
|
||||
if (e.id == IPSET_INVALID_ID)
|
||||
return -IPSET_ERR_NAME;
|
||||
/* "Loop detection" */
|
||||
@@ -389,7 +389,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
|
||||
|
||||
if (tb[IPSET_ATTR_NAMEREF]) {
|
||||
e.refid = ip_set_get_byname(map->net,
|
||||
nla_data(tb[IPSET_ATTR_NAMEREF]),
|
||||
tb[IPSET_ATTR_NAMEREF],
|
||||
&s);
|
||||
if (e.refid == IPSET_INVALID_ID) {
|
||||
ret = -IPSET_ERR_NAMEREF;
|
||||
|
||||
@@ -415,7 +415,7 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
|
||||
*/
|
||||
synchronize_rcu();
|
||||
|
||||
nf_ct_expect_iterate_destroy(expect_iter_me, NULL);
|
||||
nf_ct_expect_iterate_destroy(expect_iter_me, me);
|
||||
nf_ct_iterate_destroy(unhelp, me);
|
||||
|
||||
/* nf_ct_iterate_destroy() does an unconditional synchronize_rcu() as
|
||||
|
||||
@@ -2636,7 +2636,6 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
|
||||
|
||||
static struct nf_conntrack_expect *
|
||||
ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct,
|
||||
struct nf_conntrack_helper *helper,
|
||||
struct nf_conntrack_tuple *tuple,
|
||||
struct nf_conntrack_tuple *mask);
|
||||
|
||||
@@ -2865,7 +2864,6 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
|
||||
{
|
||||
struct nlattr *cda[CTA_EXPECT_MAX+1];
|
||||
struct nf_conntrack_tuple tuple, mask;
|
||||
struct nf_conntrack_helper *helper = NULL;
|
||||
struct nf_conntrack_expect *exp;
|
||||
int err;
|
||||
|
||||
@@ -2879,17 +2877,8 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (cda[CTA_EXPECT_HELP_NAME]) {
|
||||
const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]);
|
||||
|
||||
helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
|
||||
nf_ct_protonum(ct));
|
||||
if (helper == NULL)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct,
|
||||
helper, &tuple, &mask);
|
||||
&tuple, &mask);
|
||||
if (IS_ERR(exp))
|
||||
return PTR_ERR(exp);
|
||||
|
||||
@@ -3528,11 +3517,11 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr,
|
||||
|
||||
static struct nf_conntrack_expect *
|
||||
ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
struct nf_conntrack_helper *helper,
|
||||
struct nf_conntrack_tuple *tuple,
|
||||
struct nf_conntrack_tuple *mask)
|
||||
{
|
||||
struct net *net = read_pnet(&ct->ct_net);
|
||||
struct nf_conntrack_helper *helper;
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct nf_conn_help *help;
|
||||
u32 class = 0;
|
||||
@@ -3542,7 +3531,11 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
if (!help)
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
if (cda[CTA_EXPECT_CLASS] && helper) {
|
||||
helper = rcu_dereference(help->helper);
|
||||
if (!helper)
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
if (cda[CTA_EXPECT_CLASS]) {
|
||||
class = ntohl(nla_get_be32(cda[CTA_EXPECT_CLASS]));
|
||||
if (class > helper->expect_class_max)
|
||||
return ERR_PTR(-EINVAL);
|
||||
@@ -3576,8 +3569,6 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
#ifdef CONFIG_NF_CONNTRACK_ZONES
|
||||
exp->zone = ct->zone;
|
||||
#endif
|
||||
if (!helper)
|
||||
helper = rcu_dereference(help->helper);
|
||||
rcu_assign_pointer(exp->helper, helper);
|
||||
exp->tuple = *tuple;
|
||||
exp->mask.src.u3 = mask->src.u3;
|
||||
@@ -3588,6 +3579,12 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
|
||||
exp, nf_ct_l3num(ct));
|
||||
if (err < 0)
|
||||
goto err_out;
|
||||
#if IS_ENABLED(CONFIG_NF_NAT)
|
||||
} else {
|
||||
memset(&exp->saved_addr, 0, sizeof(exp->saved_addr));
|
||||
memset(&exp->saved_proto, 0, sizeof(exp->saved_proto));
|
||||
exp->dir = 0;
|
||||
#endif
|
||||
}
|
||||
return exp;
|
||||
err_out:
|
||||
@@ -3603,7 +3600,6 @@ ctnetlink_create_expect(struct net *net,
|
||||
{
|
||||
struct nf_conntrack_tuple tuple, mask, master_tuple;
|
||||
struct nf_conntrack_tuple_hash *h = NULL;
|
||||
struct nf_conntrack_helper *helper = NULL;
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct nf_conn *ct;
|
||||
int err;
|
||||
@@ -3629,33 +3625,7 @@ ctnetlink_create_expect(struct net *net,
|
||||
ct = nf_ct_tuplehash_to_ctrack(h);
|
||||
|
||||
rcu_read_lock();
|
||||
if (cda[CTA_EXPECT_HELP_NAME]) {
|
||||
const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]);
|
||||
|
||||
helper = __nf_conntrack_helper_find(helpname, u3,
|
||||
nf_ct_protonum(ct));
|
||||
if (helper == NULL) {
|
||||
rcu_read_unlock();
|
||||
#ifdef CONFIG_MODULES
|
||||
if (request_module("nfct-helper-%s", helpname) < 0) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto err_ct;
|
||||
}
|
||||
rcu_read_lock();
|
||||
helper = __nf_conntrack_helper_find(helpname, u3,
|
||||
nf_ct_protonum(ct));
|
||||
if (helper) {
|
||||
err = -EAGAIN;
|
||||
goto err_rcu;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
#endif
|
||||
err = -EOPNOTSUPP;
|
||||
goto err_ct;
|
||||
}
|
||||
}
|
||||
|
||||
exp = ctnetlink_alloc_expect(cda, ct, helper, &tuple, &mask);
|
||||
exp = ctnetlink_alloc_expect(cda, ct, &tuple, &mask);
|
||||
if (IS_ERR(exp)) {
|
||||
err = PTR_ERR(exp);
|
||||
goto err_rcu;
|
||||
@@ -3665,8 +3635,8 @@ ctnetlink_create_expect(struct net *net,
|
||||
nf_ct_expect_put(exp);
|
||||
err_rcu:
|
||||
rcu_read_unlock();
|
||||
err_ct:
|
||||
nf_ct_put(ct);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/nf_conntrack_tuple.h>
|
||||
|
||||
#define NF_FLOW_RULE_ACTION_MAX 24
|
||||
|
||||
static struct workqueue_struct *nf_flow_offload_add_wq;
|
||||
static struct workqueue_struct *nf_flow_offload_del_wq;
|
||||
static struct workqueue_struct *nf_flow_offload_stats_wq;
|
||||
@@ -216,7 +218,12 @@ static void flow_offload_mangle(struct flow_action_entry *entry,
|
||||
static inline struct flow_action_entry *
|
||||
flow_action_entry_next(struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
int i = flow_rule->rule->action.num_entries++;
|
||||
int i;
|
||||
|
||||
if (unlikely(flow_rule->rule->action.num_entries >= NF_FLOW_RULE_ACTION_MAX))
|
||||
return NULL;
|
||||
|
||||
i = flow_rule->rule->action.num_entries++;
|
||||
|
||||
return &flow_rule->rule->action.entries[i];
|
||||
}
|
||||
@@ -234,6 +241,9 @@ static int flow_offload_eth_src(struct net *net,
|
||||
u32 mask, val;
|
||||
u16 val16;
|
||||
|
||||
if (!entry0 || !entry1)
|
||||
return -E2BIG;
|
||||
|
||||
this_tuple = &flow->tuplehash[dir].tuple;
|
||||
|
||||
switch (this_tuple->xmit_type) {
|
||||
@@ -284,6 +294,9 @@ static int flow_offload_eth_dst(struct net *net,
|
||||
u8 nud_state;
|
||||
u16 val16;
|
||||
|
||||
if (!entry0 || !entry1)
|
||||
return -E2BIG;
|
||||
|
||||
this_tuple = &flow->tuplehash[dir].tuple;
|
||||
|
||||
switch (this_tuple->xmit_type) {
|
||||
@@ -325,16 +338,19 @@ static int flow_offload_eth_dst(struct net *net,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flow_offload_ipv4_snat(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
static int flow_offload_ipv4_snat(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
|
||||
u32 mask = ~htonl(0xffffffff);
|
||||
__be32 addr;
|
||||
u32 offset;
|
||||
|
||||
if (!entry)
|
||||
return -E2BIG;
|
||||
|
||||
switch (dir) {
|
||||
case FLOW_OFFLOAD_DIR_ORIGINAL:
|
||||
addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_v4.s_addr;
|
||||
@@ -345,23 +361,27 @@ static void flow_offload_ipv4_snat(struct net *net,
|
||||
offset = offsetof(struct iphdr, daddr);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP4, offset,
|
||||
&addr, &mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flow_offload_ipv4_dnat(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
static int flow_offload_ipv4_dnat(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
|
||||
u32 mask = ~htonl(0xffffffff);
|
||||
__be32 addr;
|
||||
u32 offset;
|
||||
|
||||
if (!entry)
|
||||
return -E2BIG;
|
||||
|
||||
switch (dir) {
|
||||
case FLOW_OFFLOAD_DIR_ORIGINAL:
|
||||
addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_v4.s_addr;
|
||||
@@ -372,14 +392,15 @@ static void flow_offload_ipv4_dnat(struct net *net,
|
||||
offset = offsetof(struct iphdr, saddr);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP4, offset,
|
||||
&addr, &mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule,
|
||||
static int flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule,
|
||||
unsigned int offset,
|
||||
const __be32 *addr, const __be32 *mask)
|
||||
{
|
||||
@@ -388,15 +409,20 @@ static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule,
|
||||
|
||||
for (i = 0; i < sizeof(struct in6_addr) / sizeof(u32); i++) {
|
||||
entry = flow_action_entry_next(flow_rule);
|
||||
if (!entry)
|
||||
return -E2BIG;
|
||||
|
||||
flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP6,
|
||||
offset + i * sizeof(u32), &addr[i], mask);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flow_offload_ipv6_snat(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
static int flow_offload_ipv6_snat(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
u32 mask = ~htonl(0xffffffff);
|
||||
const __be32 *addr;
|
||||
@@ -412,16 +438,16 @@ static void flow_offload_ipv6_snat(struct net *net,
|
||||
offset = offsetof(struct ipv6hdr, daddr);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask);
|
||||
return flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask);
|
||||
}
|
||||
|
||||
static void flow_offload_ipv6_dnat(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
static int flow_offload_ipv6_dnat(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
u32 mask = ~htonl(0xffffffff);
|
||||
const __be32 *addr;
|
||||
@@ -437,10 +463,10 @@ static void flow_offload_ipv6_dnat(struct net *net,
|
||||
offset = offsetof(struct ipv6hdr, saddr);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask);
|
||||
return flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask);
|
||||
}
|
||||
|
||||
static int flow_offload_l4proto(const struct flow_offload *flow)
|
||||
@@ -462,15 +488,18 @@ static int flow_offload_l4proto(const struct flow_offload *flow)
|
||||
return type;
|
||||
}
|
||||
|
||||
static void flow_offload_port_snat(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
static int flow_offload_port_snat(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
|
||||
u32 mask, port;
|
||||
u32 offset;
|
||||
|
||||
if (!entry)
|
||||
return -E2BIG;
|
||||
|
||||
switch (dir) {
|
||||
case FLOW_OFFLOAD_DIR_ORIGINAL:
|
||||
port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_port);
|
||||
@@ -485,22 +514,26 @@ static void flow_offload_port_snat(struct net *net,
|
||||
mask = ~htonl(0xffff);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
flow_offload_mangle(entry, flow_offload_l4proto(flow), offset,
|
||||
&port, &mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flow_offload_port_dnat(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
static int flow_offload_port_dnat(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
|
||||
u32 mask, port;
|
||||
u32 offset;
|
||||
|
||||
if (!entry)
|
||||
return -E2BIG;
|
||||
|
||||
switch (dir) {
|
||||
case FLOW_OFFLOAD_DIR_ORIGINAL:
|
||||
port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_port);
|
||||
@@ -515,20 +548,24 @@ static void flow_offload_port_dnat(struct net *net,
|
||||
mask = ~htonl(0xffff0000);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
flow_offload_mangle(entry, flow_offload_l4proto(flow), offset,
|
||||
&port, &mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flow_offload_ipv4_checksum(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
static int flow_offload_ipv4_checksum(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
u8 protonum = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.l4proto;
|
||||
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
|
||||
|
||||
if (!entry)
|
||||
return -E2BIG;
|
||||
|
||||
entry->id = FLOW_ACTION_CSUM;
|
||||
entry->csum_flags = TCA_CSUM_UPDATE_FLAG_IPV4HDR;
|
||||
|
||||
@@ -540,12 +577,14 @@ static void flow_offload_ipv4_checksum(struct net *net,
|
||||
entry->csum_flags |= TCA_CSUM_UPDATE_FLAG_UDP;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flow_offload_redirect(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
static int flow_offload_redirect(struct net *net,
|
||||
const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
const struct flow_offload_tuple *this_tuple, *other_tuple;
|
||||
struct flow_action_entry *entry;
|
||||
@@ -563,21 +602,28 @@ static void flow_offload_redirect(struct net *net,
|
||||
ifindex = other_tuple->iifidx;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
dev = dev_get_by_index(net, ifindex);
|
||||
if (!dev)
|
||||
return;
|
||||
return -ENODEV;
|
||||
|
||||
entry = flow_action_entry_next(flow_rule);
|
||||
if (!entry) {
|
||||
dev_put(dev);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
entry->id = FLOW_ACTION_REDIRECT;
|
||||
entry->dev = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flow_offload_encap_tunnel(const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
static int flow_offload_encap_tunnel(const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
const struct flow_offload_tuple *this_tuple;
|
||||
struct flow_action_entry *entry;
|
||||
@@ -585,7 +631,7 @@ static void flow_offload_encap_tunnel(const struct flow_offload *flow,
|
||||
|
||||
this_tuple = &flow->tuplehash[dir].tuple;
|
||||
if (this_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
dst = this_tuple->dst_cache;
|
||||
if (dst && dst->lwtstate) {
|
||||
@@ -594,15 +640,19 @@ static void flow_offload_encap_tunnel(const struct flow_offload *flow,
|
||||
tun_info = lwt_tun_info(dst->lwtstate);
|
||||
if (tun_info && (tun_info->mode & IP_TUNNEL_INFO_TX)) {
|
||||
entry = flow_action_entry_next(flow_rule);
|
||||
if (!entry)
|
||||
return -E2BIG;
|
||||
entry->id = FLOW_ACTION_TUNNEL_ENCAP;
|
||||
entry->tunnel = tun_info;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flow_offload_decap_tunnel(const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
static int flow_offload_decap_tunnel(const struct flow_offload *flow,
|
||||
enum flow_offload_tuple_dir dir,
|
||||
struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
const struct flow_offload_tuple *other_tuple;
|
||||
struct flow_action_entry *entry;
|
||||
@@ -610,7 +660,7 @@ static void flow_offload_decap_tunnel(const struct flow_offload *flow,
|
||||
|
||||
other_tuple = &flow->tuplehash[!dir].tuple;
|
||||
if (other_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
dst = other_tuple->dst_cache;
|
||||
if (dst && dst->lwtstate) {
|
||||
@@ -619,9 +669,13 @@ static void flow_offload_decap_tunnel(const struct flow_offload *flow,
|
||||
tun_info = lwt_tun_info(dst->lwtstate);
|
||||
if (tun_info && (tun_info->mode & IP_TUNNEL_INFO_TX)) {
|
||||
entry = flow_action_entry_next(flow_rule);
|
||||
if (!entry)
|
||||
return -E2BIG;
|
||||
entry->id = FLOW_ACTION_TUNNEL_DECAP;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -633,8 +687,9 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
|
||||
const struct flow_offload_tuple *tuple;
|
||||
int i;
|
||||
|
||||
flow_offload_decap_tunnel(flow, dir, flow_rule);
|
||||
flow_offload_encap_tunnel(flow, dir, flow_rule);
|
||||
if (flow_offload_decap_tunnel(flow, dir, flow_rule) < 0 ||
|
||||
flow_offload_encap_tunnel(flow, dir, flow_rule) < 0)
|
||||
return -1;
|
||||
|
||||
if (flow_offload_eth_src(net, flow, dir, flow_rule) < 0 ||
|
||||
flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
|
||||
@@ -650,6 +705,8 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
|
||||
|
||||
if (tuple->encap[i].proto == htons(ETH_P_8021Q)) {
|
||||
entry = flow_action_entry_next(flow_rule);
|
||||
if (!entry)
|
||||
return -1;
|
||||
entry->id = FLOW_ACTION_VLAN_POP;
|
||||
}
|
||||
}
|
||||
@@ -663,6 +720,8 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
|
||||
continue;
|
||||
|
||||
entry = flow_action_entry_next(flow_rule);
|
||||
if (!entry)
|
||||
return -1;
|
||||
|
||||
switch (other_tuple->encap[i].proto) {
|
||||
case htons(ETH_P_PPP_SES):
|
||||
@@ -688,18 +747,22 @@ int nf_flow_rule_route_ipv4(struct net *net, struct flow_offload *flow,
|
||||
return -1;
|
||||
|
||||
if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
|
||||
flow_offload_ipv4_snat(net, flow, dir, flow_rule);
|
||||
flow_offload_port_snat(net, flow, dir, flow_rule);
|
||||
if (flow_offload_ipv4_snat(net, flow, dir, flow_rule) < 0 ||
|
||||
flow_offload_port_snat(net, flow, dir, flow_rule) < 0)
|
||||
return -1;
|
||||
}
|
||||
if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
|
||||
flow_offload_ipv4_dnat(net, flow, dir, flow_rule);
|
||||
flow_offload_port_dnat(net, flow, dir, flow_rule);
|
||||
if (flow_offload_ipv4_dnat(net, flow, dir, flow_rule) < 0 ||
|
||||
flow_offload_port_dnat(net, flow, dir, flow_rule) < 0)
|
||||
return -1;
|
||||
}
|
||||
if (test_bit(NF_FLOW_SNAT, &flow->flags) ||
|
||||
test_bit(NF_FLOW_DNAT, &flow->flags))
|
||||
flow_offload_ipv4_checksum(net, flow, flow_rule);
|
||||
if (flow_offload_ipv4_checksum(net, flow, flow_rule) < 0)
|
||||
return -1;
|
||||
|
||||
flow_offload_redirect(net, flow, dir, flow_rule);
|
||||
if (flow_offload_redirect(net, flow, dir, flow_rule) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -713,22 +776,23 @@ int nf_flow_rule_route_ipv6(struct net *net, struct flow_offload *flow,
|
||||
return -1;
|
||||
|
||||
if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
|
||||
flow_offload_ipv6_snat(net, flow, dir, flow_rule);
|
||||
flow_offload_port_snat(net, flow, dir, flow_rule);
|
||||
if (flow_offload_ipv6_snat(net, flow, dir, flow_rule) < 0 ||
|
||||
flow_offload_port_snat(net, flow, dir, flow_rule) < 0)
|
||||
return -1;
|
||||
}
|
||||
if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
|
||||
flow_offload_ipv6_dnat(net, flow, dir, flow_rule);
|
||||
flow_offload_port_dnat(net, flow, dir, flow_rule);
|
||||
if (flow_offload_ipv6_dnat(net, flow, dir, flow_rule) < 0 ||
|
||||
flow_offload_port_dnat(net, flow, dir, flow_rule) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
flow_offload_redirect(net, flow, dir, flow_rule);
|
||||
if (flow_offload_redirect(net, flow, dir, flow_rule) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_flow_rule_route_ipv6);
|
||||
|
||||
#define NF_FLOW_RULE_ACTION_MAX 16
|
||||
|
||||
static struct nf_flow_rule *
|
||||
nf_flow_offload_rule_alloc(struct net *net,
|
||||
const struct flow_offload_work *offload,
|
||||
|
||||
@@ -11667,8 +11667,6 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
|
||||
switch (data->verdict.code) {
|
||||
case NF_ACCEPT:
|
||||
case NF_DROP:
|
||||
case NF_QUEUE:
|
||||
break;
|
||||
case NFT_CONTINUE:
|
||||
case NFT_BREAK:
|
||||
case NFT_RETURN:
|
||||
@@ -11703,6 +11701,11 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
|
||||
|
||||
data->verdict.chain = chain;
|
||||
break;
|
||||
case NF_QUEUE:
|
||||
/* The nft_queue expression is used for this purpose, an
|
||||
* immediate NF_QUEUE verdict should not ever be seen here.
|
||||
*/
|
||||
fallthrough;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -726,7 +726,7 @@ nfulnl_log_packet(struct net *net,
|
||||
+ nla_total_size(plen) /* prefix */
|
||||
+ nla_total_size(sizeof(struct nfulnl_msg_packet_hw))
|
||||
+ nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp))
|
||||
+ nla_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */
|
||||
+ nlmsg_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */
|
||||
|
||||
if (in && skb_mac_header_was_set(skb)) {
|
||||
size += nla_total_size(skb->dev->hard_header_len)
|
||||
|
||||
@@ -501,6 +501,17 @@ int xt_check_match(struct xt_mtchk_param *par,
|
||||
par->match->table, par->table);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* NFPROTO_UNSPEC implies NF_INET_* hooks which do not overlap with
|
||||
* NF_ARP_IN,OUT,FORWARD, allow explicit extensions with NFPROTO_ARP
|
||||
* support.
|
||||
*/
|
||||
if (par->family == NFPROTO_ARP &&
|
||||
par->match->family != NFPROTO_ARP) {
|
||||
pr_info_ratelimited("%s_tables: %s match: not valid for this family\n",
|
||||
xt_prefix[par->family], par->match->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) {
|
||||
char used[64], allow[64];
|
||||
|
||||
@@ -1016,6 +1027,18 @@ int xt_check_target(struct xt_tgchk_param *par,
|
||||
par->target->table, par->table);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* NFPROTO_UNSPEC implies NF_INET_* hooks which do not overlap with
|
||||
* NF_ARP_IN,OUT,FORWARD, allow explicit extensions with NFPROTO_ARP
|
||||
* support.
|
||||
*/
|
||||
if (par->family == NFPROTO_ARP &&
|
||||
par->target->family != NFPROTO_ARP) {
|
||||
pr_info_ratelimited("%s_tables: %s target: not valid for this family\n",
|
||||
xt_prefix[par->family], par->target->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (par->target->hooks && (par->hook_mask & ~par->target->hooks) != 0) {
|
||||
char used[64], allow[64];
|
||||
|
||||
|
||||
@@ -65,6 +65,9 @@ static int cgroup_mt_check_v1(const struct xt_mtchk_param *par)
|
||||
|
||||
info->priv = NULL;
|
||||
if (info->has_path) {
|
||||
if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
cgrp = cgroup_get_from_path(info->path);
|
||||
if (IS_ERR(cgrp)) {
|
||||
pr_info_ratelimited("invalid path, errno=%ld\n",
|
||||
@@ -102,6 +105,9 @@ static int cgroup_mt_check_v2(const struct xt_mtchk_param *par)
|
||||
|
||||
info->priv = NULL;
|
||||
if (info->has_path) {
|
||||
if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
cgrp = cgroup_get_from_path(info->path);
|
||||
if (IS_ERR(cgrp)) {
|
||||
pr_info_ratelimited("invalid path, errno=%ld\n",
|
||||
|
||||
@@ -91,6 +91,11 @@ static int xt_rateest_mt_checkentry(const struct xt_mtchk_param *par)
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (strnlen(info->name1, sizeof(info->name1)) >= sizeof(info->name1))
|
||||
return -ENAMETOOLONG;
|
||||
if (strnlen(info->name2, sizeof(info->name2)) >= sizeof(info->name2))
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
ret = -ENOENT;
|
||||
est1 = xt_rateest_lookup(par->net, info->name1);
|
||||
if (!est1)
|
||||
|
||||
@@ -604,8 +604,13 @@ void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents,
|
||||
return ibmr;
|
||||
}
|
||||
|
||||
if (conn)
|
||||
if (conn) {
|
||||
ic = conn->c_transport_data;
|
||||
if (!ic || !ic->i_cm_id || !ic->i_cm_id->qp) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rds_ibdev->mr_8k_pool || !rds_ibdev->mr_1m_pool) {
|
||||
ret = -ENODEV;
|
||||
|
||||
Reference in New Issue
Block a user