.. | .. |
---|
1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
---|
2 | | -/* Copyright (C) 2011-2018 B.A.T.M.A.N. contributors: |
---|
| 2 | +/* Copyright (C) 2011-2020 B.A.T.M.A.N. contributors: |
---|
3 | 3 | * |
---|
4 | 4 | * Antonio Quartulli |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or |
---|
7 | | - * modify it under the terms of version 2 of the GNU General Public |
---|
8 | | - * License as published by the Free Software Foundation. |
---|
9 | | - * |
---|
10 | | - * This program is distributed in the hope that it will be useful, but |
---|
11 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
12 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
13 | | - * General Public License for more details. |
---|
14 | | - * |
---|
15 | | - * You should have received a copy of the GNU General Public License |
---|
16 | | - * along with this program; if not, see <http://www.gnu.org/licenses/>. |
---|
17 | 5 | */ |
---|
18 | 6 | |
---|
19 | 7 | #include "distributed-arp-table.h" |
---|
20 | 8 | #include "main.h" |
---|
21 | 9 | |
---|
| 10 | +#include <asm/unaligned.h> |
---|
22 | 11 | #include <linux/atomic.h> |
---|
23 | 12 | #include <linux/bitops.h> |
---|
24 | 13 | #include <linux/byteorder/generic.h> |
---|
.. | .. |
---|
29 | 18 | #include <linux/if_ether.h> |
---|
30 | 19 | #include <linux/if_vlan.h> |
---|
31 | 20 | #include <linux/in.h> |
---|
| 21 | +#include <linux/ip.h> |
---|
32 | 22 | #include <linux/jiffies.h> |
---|
33 | 23 | #include <linux/kernel.h> |
---|
34 | 24 | #include <linux/kref.h> |
---|
.. | .. |
---|
42 | 32 | #include <linux/spinlock.h> |
---|
43 | 33 | #include <linux/stddef.h> |
---|
44 | 34 | #include <linux/string.h> |
---|
| 35 | +#include <linux/udp.h> |
---|
45 | 36 | #include <linux/workqueue.h> |
---|
46 | 37 | #include <net/arp.h> |
---|
47 | 38 | #include <net/genetlink.h> |
---|
.. | .. |
---|
60 | 51 | #include "translation-table.h" |
---|
61 | 52 | #include "tvlv.h" |
---|
62 | 53 | |
---|
| 54 | +enum batadv_bootpop { |
---|
| 55 | + BATADV_BOOTREPLY = 2, |
---|
| 56 | +}; |
---|
| 57 | + |
---|
| 58 | +enum batadv_boothtype { |
---|
| 59 | + BATADV_HTYPE_ETHERNET = 1, |
---|
| 60 | +}; |
---|
| 61 | + |
---|
| 62 | +enum batadv_dhcpoptioncode { |
---|
| 63 | + BATADV_DHCP_OPT_PAD = 0, |
---|
| 64 | + BATADV_DHCP_OPT_MSG_TYPE = 53, |
---|
| 65 | + BATADV_DHCP_OPT_END = 255, |
---|
| 66 | +}; |
---|
| 67 | + |
---|
| 68 | +enum batadv_dhcptype { |
---|
| 69 | + BATADV_DHCPACK = 5, |
---|
| 70 | +}; |
---|
| 71 | + |
---|
| 72 | +/* { 99, 130, 83, 99 } */ |
---|
| 73 | +#define BATADV_DHCP_MAGIC 1669485411 |
---|
| 74 | + |
---|
| 75 | +struct batadv_dhcp_packet { |
---|
| 76 | + __u8 op; |
---|
| 77 | + __u8 htype; |
---|
| 78 | + __u8 hlen; |
---|
| 79 | + __u8 hops; |
---|
| 80 | + __be32 xid; |
---|
| 81 | + __be16 secs; |
---|
| 82 | + __be16 flags; |
---|
| 83 | + __be32 ciaddr; |
---|
| 84 | + __be32 yiaddr; |
---|
| 85 | + __be32 siaddr; |
---|
| 86 | + __be32 giaddr; |
---|
| 87 | + __u8 chaddr[16]; |
---|
| 88 | + __u8 sname[64]; |
---|
| 89 | + __u8 file[128]; |
---|
| 90 | + __be32 magic; |
---|
| 91 | + __u8 options[]; |
---|
| 92 | +}; |
---|
| 93 | + |
---|
| 94 | +#define BATADV_DHCP_YIADDR_LEN sizeof(((struct batadv_dhcp_packet *)0)->yiaddr) |
---|
| 95 | +#define BATADV_DHCP_CHADDR_LEN sizeof(((struct batadv_dhcp_packet *)0)->chaddr) |
---|
| 96 | + |
---|
63 | 97 | static void batadv_dat_purge(struct work_struct *work); |
---|
64 | 98 | |
---|
65 | 99 | /** |
---|
.. | .. |
---|
68 | 102 | */ |
---|
69 | 103 | static void batadv_dat_start_timer(struct batadv_priv *bat_priv) |
---|
70 | 104 | { |
---|
71 | | - INIT_DELAYED_WORK(&bat_priv->dat.work, batadv_dat_purge); |
---|
72 | 105 | queue_delayed_work(batadv_event_workqueue, &bat_priv->dat.work, |
---|
73 | 106 | msecs_to_jiffies(10000)); |
---|
74 | 107 | } |
---|
.. | .. |
---|
94 | 127 | */ |
---|
95 | 128 | static void batadv_dat_entry_put(struct batadv_dat_entry *dat_entry) |
---|
96 | 129 | { |
---|
| 130 | + if (!dat_entry) |
---|
| 131 | + return; |
---|
| 132 | + |
---|
97 | 133 | kref_put(&dat_entry->refcount, batadv_dat_entry_release); |
---|
98 | 134 | } |
---|
99 | 135 | |
---|
.. | .. |
---|
212 | 248 | */ |
---|
213 | 249 | static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size) |
---|
214 | 250 | { |
---|
215 | | - return *(__be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN); |
---|
| 251 | + return *(__force __be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN); |
---|
216 | 252 | } |
---|
217 | 253 | |
---|
218 | 254 | /** |
---|
.. | .. |
---|
236 | 272 | */ |
---|
237 | 273 | static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size) |
---|
238 | 274 | { |
---|
239 | | - return *(__be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN * 2 + 4); |
---|
| 275 | + u8 *dst = batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN * 2 + 4; |
---|
| 276 | + |
---|
| 277 | + return *(__force __be32 *)dst; |
---|
240 | 278 | } |
---|
241 | 279 | |
---|
242 | 280 | /** |
---|
.. | .. |
---|
254 | 292 | __be16 vid; |
---|
255 | 293 | u32 i; |
---|
256 | 294 | |
---|
257 | | - key = (const unsigned char *)&dat->ip; |
---|
| 295 | + key = (__force const unsigned char *)&dat->ip; |
---|
258 | 296 | for (i = 0; i < sizeof(dat->ip); i++) { |
---|
259 | 297 | hash += key[i]; |
---|
260 | 298 | hash += (hash << 10); |
---|
.. | .. |
---|
623 | 661 | } |
---|
624 | 662 | |
---|
625 | 663 | /** |
---|
626 | | - * batadv_dat_send_data() - send a payload to the selected candidates |
---|
| 664 | + * batadv_dat_forward_data() - copy and send payload to the selected candidates |
---|
627 | 665 | * @bat_priv: the bat priv with all the soft interface information |
---|
628 | 666 | * @skb: payload to send |
---|
629 | 667 | * @ip: the DHT key |
---|
630 | 668 | * @vid: VLAN identifier |
---|
631 | 669 | * @packet_subtype: unicast4addr packet subtype to use |
---|
632 | 670 | * |
---|
633 | | - * This function copies the skb with pskb_copy() and is sent as unicast packet |
---|
| 671 | + * This function copies the skb with pskb_copy() and is sent as a unicast packet |
---|
634 | 672 | * to each of the selected candidates. |
---|
635 | 673 | * |
---|
636 | 674 | * Return: true if the packet is sent to at least one candidate, false |
---|
637 | 675 | * otherwise. |
---|
638 | 676 | */ |
---|
639 | | -static bool batadv_dat_send_data(struct batadv_priv *bat_priv, |
---|
640 | | - struct sk_buff *skb, __be32 ip, |
---|
641 | | - unsigned short vid, int packet_subtype) |
---|
| 677 | +static bool batadv_dat_forward_data(struct batadv_priv *bat_priv, |
---|
| 678 | + struct sk_buff *skb, __be32 ip, |
---|
| 679 | + unsigned short vid, int packet_subtype) |
---|
642 | 680 | { |
---|
643 | 681 | int i; |
---|
644 | 682 | bool ret = false; |
---|
.. | .. |
---|
783 | 821 | if (!bat_priv->dat.hash) |
---|
784 | 822 | return -ENOMEM; |
---|
785 | 823 | |
---|
| 824 | + INIT_DELAYED_WORK(&bat_priv->dat.work, batadv_dat_purge); |
---|
786 | 825 | batadv_dat_start_timer(bat_priv); |
---|
787 | 826 | |
---|
788 | 827 | batadv_tvlv_handler_register(bat_priv, batadv_dat_tvlv_ogm_handler_v1, |
---|
.. | .. |
---|
865 | 904 | * netlink socket |
---|
866 | 905 | * @msg: buffer for the message |
---|
867 | 906 | * @portid: netlink port |
---|
868 | | - * @seq: Sequence number of netlink message |
---|
| 907 | + * @cb: Control block containing additional options |
---|
869 | 908 | * @dat_entry: entry to dump |
---|
870 | 909 | * |
---|
871 | 910 | * Return: 0 or error code. |
---|
872 | 911 | */ |
---|
873 | 912 | static int |
---|
874 | | -batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid, u32 seq, |
---|
| 913 | +batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid, |
---|
| 914 | + struct netlink_callback *cb, |
---|
875 | 915 | struct batadv_dat_entry *dat_entry) |
---|
876 | 916 | { |
---|
877 | 917 | int msecs; |
---|
878 | 918 | void *hdr; |
---|
879 | 919 | |
---|
880 | | - hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, |
---|
881 | | - NLM_F_MULTI, BATADV_CMD_GET_DAT_CACHE); |
---|
| 920 | + hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq, |
---|
| 921 | + &batadv_netlink_family, NLM_F_MULTI, |
---|
| 922 | + BATADV_CMD_GET_DAT_CACHE); |
---|
882 | 923 | if (!hdr) |
---|
883 | 924 | return -ENOBUFS; |
---|
| 925 | + |
---|
| 926 | + genl_dump_check_consistent(cb, hdr); |
---|
884 | 927 | |
---|
885 | 928 | msecs = jiffies_to_msecs(jiffies - dat_entry->last_update); |
---|
886 | 929 | |
---|
.. | .. |
---|
903 | 946 | * a netlink socket |
---|
904 | 947 | * @msg: buffer for the message |
---|
905 | 948 | * @portid: netlink port |
---|
906 | | - * @seq: Sequence number of netlink message |
---|
907 | | - * @head: bucket to dump |
---|
| 949 | + * @cb: Control block containing additional options |
---|
| 950 | + * @hash: hash to dump |
---|
| 951 | + * @bucket: bucket index to dump |
---|
908 | 952 | * @idx_skip: How many entries to skip |
---|
909 | 953 | * |
---|
910 | 954 | * Return: 0 or error code. |
---|
911 | 955 | */ |
---|
912 | 956 | static int |
---|
913 | | -batadv_dat_cache_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq, |
---|
914 | | - struct hlist_head *head, int *idx_skip) |
---|
| 957 | +batadv_dat_cache_dump_bucket(struct sk_buff *msg, u32 portid, |
---|
| 958 | + struct netlink_callback *cb, |
---|
| 959 | + struct batadv_hashtable *hash, unsigned int bucket, |
---|
| 960 | + int *idx_skip) |
---|
915 | 961 | { |
---|
916 | 962 | struct batadv_dat_entry *dat_entry; |
---|
917 | 963 | int idx = 0; |
---|
918 | 964 | |
---|
919 | | - rcu_read_lock(); |
---|
920 | | - hlist_for_each_entry_rcu(dat_entry, head, hash_entry) { |
---|
| 965 | + spin_lock_bh(&hash->list_locks[bucket]); |
---|
| 966 | + cb->seq = atomic_read(&hash->generation) << 1 | 1; |
---|
| 967 | + |
---|
| 968 | + hlist_for_each_entry(dat_entry, &hash->table[bucket], hash_entry) { |
---|
921 | 969 | if (idx < *idx_skip) |
---|
922 | 970 | goto skip; |
---|
923 | 971 | |
---|
924 | | - if (batadv_dat_cache_dump_entry(msg, portid, seq, |
---|
925 | | - dat_entry)) { |
---|
926 | | - rcu_read_unlock(); |
---|
| 972 | + if (batadv_dat_cache_dump_entry(msg, portid, cb, dat_entry)) { |
---|
| 973 | + spin_unlock_bh(&hash->list_locks[bucket]); |
---|
927 | 974 | *idx_skip = idx; |
---|
928 | 975 | |
---|
929 | 976 | return -EMSGSIZE; |
---|
.. | .. |
---|
932 | 979 | skip: |
---|
933 | 980 | idx++; |
---|
934 | 981 | } |
---|
935 | | - rcu_read_unlock(); |
---|
| 982 | + spin_unlock_bh(&hash->list_locks[bucket]); |
---|
936 | 983 | |
---|
937 | 984 | return 0; |
---|
938 | 985 | } |
---|
.. | .. |
---|
953 | 1000 | struct batadv_hashtable *hash; |
---|
954 | 1001 | struct batadv_priv *bat_priv; |
---|
955 | 1002 | int bucket = cb->args[0]; |
---|
956 | | - struct hlist_head *head; |
---|
957 | 1003 | int idx = cb->args[1]; |
---|
958 | 1004 | int ifindex; |
---|
959 | 1005 | int ret = 0; |
---|
.. | .. |
---|
979 | 1025 | } |
---|
980 | 1026 | |
---|
981 | 1027 | while (bucket < hash->size) { |
---|
982 | | - head = &hash->table[bucket]; |
---|
983 | | - |
---|
984 | | - if (batadv_dat_cache_dump_bucket(msg, portid, |
---|
985 | | - cb->nlh->nlmsg_seq, head, |
---|
| 1028 | + if (batadv_dat_cache_dump_bucket(msg, portid, cb, hash, bucket, |
---|
986 | 1029 | &idx)) |
---|
987 | 1030 | break; |
---|
988 | 1031 | |
---|
.. | .. |
---|
1229 | 1272 | ret = true; |
---|
1230 | 1273 | } else { |
---|
1231 | 1274 | /* Send the request to the DHT */ |
---|
1232 | | - ret = batadv_dat_send_data(bat_priv, skb, ip_dst, vid, |
---|
1233 | | - BATADV_P_DAT_DHT_GET); |
---|
| 1275 | + ret = batadv_dat_forward_data(bat_priv, skb, ip_dst, vid, |
---|
| 1276 | + BATADV_P_DAT_DHT_GET); |
---|
1234 | 1277 | } |
---|
1235 | 1278 | out: |
---|
1236 | 1279 | if (dat_entry) |
---|
.. | .. |
---|
1344 | 1387 | /* Send the ARP reply to the candidates for both the IP addresses that |
---|
1345 | 1388 | * the node obtained from the ARP reply |
---|
1346 | 1389 | */ |
---|
1347 | | - batadv_dat_send_data(bat_priv, skb, ip_src, vid, BATADV_P_DAT_DHT_PUT); |
---|
1348 | | - batadv_dat_send_data(bat_priv, skb, ip_dst, vid, BATADV_P_DAT_DHT_PUT); |
---|
| 1390 | + batadv_dat_forward_data(bat_priv, skb, ip_src, vid, |
---|
| 1391 | + BATADV_P_DAT_DHT_PUT); |
---|
| 1392 | + batadv_dat_forward_data(bat_priv, skb, ip_dst, vid, |
---|
| 1393 | + BATADV_P_DAT_DHT_PUT); |
---|
1349 | 1394 | } |
---|
1350 | 1395 | |
---|
1351 | 1396 | /** |
---|
.. | .. |
---|
1440 | 1485 | } |
---|
1441 | 1486 | |
---|
1442 | 1487 | /** |
---|
| 1488 | + * batadv_dat_check_dhcp_ipudp() - check skb for IP+UDP headers valid for DHCP |
---|
| 1489 | + * @skb: the packet to check |
---|
| 1490 | + * @ip_src: a buffer to store the IPv4 source address in |
---|
| 1491 | + * |
---|
| 1492 | + * Checks whether the given skb has an IP and UDP header valid for a DHCP |
---|
| 1493 | + * message from a DHCP server. And if so, stores the IPv4 source address in |
---|
| 1494 | + * the provided buffer. |
---|
| 1495 | + * |
---|
| 1496 | + * Return: True if valid, false otherwise. |
---|
| 1497 | + */ |
---|
| 1498 | +static bool |
---|
| 1499 | +batadv_dat_check_dhcp_ipudp(struct sk_buff *skb, __be32 *ip_src) |
---|
| 1500 | +{ |
---|
| 1501 | + unsigned int offset = skb_network_offset(skb); |
---|
| 1502 | + struct udphdr *udphdr, _udphdr; |
---|
| 1503 | + struct iphdr *iphdr, _iphdr; |
---|
| 1504 | + |
---|
| 1505 | + iphdr = skb_header_pointer(skb, offset, sizeof(_iphdr), &_iphdr); |
---|
| 1506 | + if (!iphdr || iphdr->version != 4 || iphdr->ihl * 4 < sizeof(_iphdr)) |
---|
| 1507 | + return false; |
---|
| 1508 | + |
---|
| 1509 | + if (iphdr->protocol != IPPROTO_UDP) |
---|
| 1510 | + return false; |
---|
| 1511 | + |
---|
| 1512 | + offset += iphdr->ihl * 4; |
---|
| 1513 | + skb_set_transport_header(skb, offset); |
---|
| 1514 | + |
---|
| 1515 | + udphdr = skb_header_pointer(skb, offset, sizeof(_udphdr), &_udphdr); |
---|
| 1516 | + if (!udphdr || udphdr->source != htons(67)) |
---|
| 1517 | + return false; |
---|
| 1518 | + |
---|
| 1519 | + *ip_src = get_unaligned(&iphdr->saddr); |
---|
| 1520 | + |
---|
| 1521 | + return true; |
---|
| 1522 | +} |
---|
| 1523 | + |
---|
| 1524 | +/** |
---|
| 1525 | + * batadv_dat_check_dhcp() - examine packet for valid DHCP message |
---|
| 1526 | + * @skb: the packet to check |
---|
| 1527 | + * @proto: ethernet protocol hint (behind a potential vlan) |
---|
| 1528 | + * @ip_src: a buffer to store the IPv4 source address in |
---|
| 1529 | + * |
---|
| 1530 | + * Checks whether the given skb is a valid DHCP packet. And if so, stores the |
---|
| 1531 | + * IPv4 source address in the provided buffer. |
---|
| 1532 | + * |
---|
| 1533 | + * Caller needs to ensure that the skb network header is set correctly. |
---|
| 1534 | + * |
---|
| 1535 | + * Return: If skb is a valid DHCP packet, then returns its op code |
---|
| 1536 | + * (e.g. BOOTREPLY vs. BOOTREQUEST). Otherwise returns -EINVAL. |
---|
| 1537 | + */ |
---|
| 1538 | +static int |
---|
| 1539 | +batadv_dat_check_dhcp(struct sk_buff *skb, __be16 proto, __be32 *ip_src) |
---|
| 1540 | +{ |
---|
| 1541 | + __be32 *magic, _magic; |
---|
| 1542 | + unsigned int offset; |
---|
| 1543 | + struct { |
---|
| 1544 | + __u8 op; |
---|
| 1545 | + __u8 htype; |
---|
| 1546 | + __u8 hlen; |
---|
| 1547 | + __u8 hops; |
---|
| 1548 | + } *dhcp_h, _dhcp_h; |
---|
| 1549 | + |
---|
| 1550 | + if (proto != htons(ETH_P_IP)) |
---|
| 1551 | + return -EINVAL; |
---|
| 1552 | + |
---|
| 1553 | + if (!batadv_dat_check_dhcp_ipudp(skb, ip_src)) |
---|
| 1554 | + return -EINVAL; |
---|
| 1555 | + |
---|
| 1556 | + offset = skb_transport_offset(skb) + sizeof(struct udphdr); |
---|
| 1557 | + if (skb->len < offset + sizeof(struct batadv_dhcp_packet)) |
---|
| 1558 | + return -EINVAL; |
---|
| 1559 | + |
---|
| 1560 | + dhcp_h = skb_header_pointer(skb, offset, sizeof(_dhcp_h), &_dhcp_h); |
---|
| 1561 | + if (!dhcp_h || dhcp_h->htype != BATADV_HTYPE_ETHERNET || |
---|
| 1562 | + dhcp_h->hlen != ETH_ALEN) |
---|
| 1563 | + return -EINVAL; |
---|
| 1564 | + |
---|
| 1565 | + offset += offsetof(struct batadv_dhcp_packet, magic); |
---|
| 1566 | + |
---|
| 1567 | + magic = skb_header_pointer(skb, offset, sizeof(_magic), &_magic); |
---|
| 1568 | + if (!magic || get_unaligned(magic) != htonl(BATADV_DHCP_MAGIC)) |
---|
| 1569 | + return -EINVAL; |
---|
| 1570 | + |
---|
| 1571 | + return dhcp_h->op; |
---|
| 1572 | +} |
---|
| 1573 | + |
---|
| 1574 | +/** |
---|
| 1575 | + * batadv_dat_get_dhcp_message_type() - get message type of a DHCP packet |
---|
| 1576 | + * @skb: the DHCP packet to parse |
---|
| 1577 | + * |
---|
| 1578 | + * Iterates over the DHCP options of the given DHCP packet to find a |
---|
| 1579 | + * DHCP Message Type option and parse it. |
---|
| 1580 | + * |
---|
| 1581 | + * Caller needs to ensure that the given skb is a valid DHCP packet and |
---|
| 1582 | + * that the skb transport header is set correctly. |
---|
| 1583 | + * |
---|
| 1584 | + * Return: The found DHCP message type value, if found. -EINVAL otherwise. |
---|
| 1585 | + */ |
---|
| 1586 | +static int batadv_dat_get_dhcp_message_type(struct sk_buff *skb) |
---|
| 1587 | +{ |
---|
| 1588 | + unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr); |
---|
| 1589 | + u8 *type, _type; |
---|
| 1590 | + struct { |
---|
| 1591 | + u8 type; |
---|
| 1592 | + u8 len; |
---|
| 1593 | + } *tl, _tl; |
---|
| 1594 | + |
---|
| 1595 | + offset += sizeof(struct batadv_dhcp_packet); |
---|
| 1596 | + |
---|
| 1597 | + while ((tl = skb_header_pointer(skb, offset, sizeof(_tl), &_tl))) { |
---|
| 1598 | + if (tl->type == BATADV_DHCP_OPT_MSG_TYPE) |
---|
| 1599 | + break; |
---|
| 1600 | + |
---|
| 1601 | + if (tl->type == BATADV_DHCP_OPT_END) |
---|
| 1602 | + break; |
---|
| 1603 | + |
---|
| 1604 | + if (tl->type == BATADV_DHCP_OPT_PAD) |
---|
| 1605 | + offset++; |
---|
| 1606 | + else |
---|
| 1607 | + offset += tl->len + sizeof(_tl); |
---|
| 1608 | + } |
---|
| 1609 | + |
---|
| 1610 | + /* Option Overload Code not supported */ |
---|
| 1611 | + if (!tl || tl->type != BATADV_DHCP_OPT_MSG_TYPE || |
---|
| 1612 | + tl->len != sizeof(_type)) |
---|
| 1613 | + return -EINVAL; |
---|
| 1614 | + |
---|
| 1615 | + offset += sizeof(_tl); |
---|
| 1616 | + |
---|
| 1617 | + type = skb_header_pointer(skb, offset, sizeof(_type), &_type); |
---|
| 1618 | + if (!type) |
---|
| 1619 | + return -EINVAL; |
---|
| 1620 | + |
---|
| 1621 | + return *type; |
---|
| 1622 | +} |
---|
| 1623 | + |
---|
| 1624 | +/** |
---|
| 1625 | + * batadv_dat_get_dhcp_yiaddr() - get yiaddr from a DHCP packet |
---|
| 1626 | + * @skb: the DHCP packet to parse |
---|
| 1627 | + * @buf: a buffer to store the yiaddr in |
---|
| 1628 | + * |
---|
| 1629 | + * Caller needs to ensure that the given skb is a valid DHCP packet and |
---|
| 1630 | + * that the skb transport header is set correctly. |
---|
| 1631 | + * |
---|
| 1632 | + * Return: True on success, false otherwise. |
---|
| 1633 | + */ |
---|
| 1634 | +static bool batadv_dat_dhcp_get_yiaddr(struct sk_buff *skb, __be32 *buf) |
---|
| 1635 | +{ |
---|
| 1636 | + unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr); |
---|
| 1637 | + __be32 *yiaddr; |
---|
| 1638 | + |
---|
| 1639 | + offset += offsetof(struct batadv_dhcp_packet, yiaddr); |
---|
| 1640 | + yiaddr = skb_header_pointer(skb, offset, BATADV_DHCP_YIADDR_LEN, buf); |
---|
| 1641 | + |
---|
| 1642 | + if (!yiaddr) |
---|
| 1643 | + return false; |
---|
| 1644 | + |
---|
| 1645 | + if (yiaddr != buf) |
---|
| 1646 | + *buf = get_unaligned(yiaddr); |
---|
| 1647 | + |
---|
| 1648 | + return true; |
---|
| 1649 | +} |
---|
| 1650 | + |
---|
| 1651 | +/** |
---|
| 1652 | + * batadv_dat_get_dhcp_chaddr() - get chaddr from a DHCP packet |
---|
| 1653 | + * @skb: the DHCP packet to parse |
---|
| 1654 | + * @buf: a buffer to store the chaddr in |
---|
| 1655 | + * |
---|
| 1656 | + * Caller needs to ensure that the given skb is a valid DHCP packet and |
---|
| 1657 | + * that the skb transport header is set correctly. |
---|
| 1658 | + * |
---|
| 1659 | + * Return: True on success, false otherwise |
---|
| 1660 | + */ |
---|
| 1661 | +static bool batadv_dat_get_dhcp_chaddr(struct sk_buff *skb, u8 *buf) |
---|
| 1662 | +{ |
---|
| 1663 | + unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr); |
---|
| 1664 | + u8 *chaddr; |
---|
| 1665 | + |
---|
| 1666 | + offset += offsetof(struct batadv_dhcp_packet, chaddr); |
---|
| 1667 | + chaddr = skb_header_pointer(skb, offset, BATADV_DHCP_CHADDR_LEN, buf); |
---|
| 1668 | + |
---|
| 1669 | + if (!chaddr) |
---|
| 1670 | + return false; |
---|
| 1671 | + |
---|
| 1672 | + if (chaddr != buf) |
---|
| 1673 | + memcpy(buf, chaddr, BATADV_DHCP_CHADDR_LEN); |
---|
| 1674 | + |
---|
| 1675 | + return true; |
---|
| 1676 | +} |
---|
| 1677 | + |
---|
| 1678 | +/** |
---|
| 1679 | + * batadv_dat_put_dhcp() - puts addresses from a DHCP packet into the DHT and |
---|
| 1680 | + * DAT cache |
---|
| 1681 | + * @bat_priv: the bat priv with all the soft interface information |
---|
| 1682 | + * @chaddr: the DHCP client MAC address |
---|
| 1683 | + * @yiaddr: the DHCP client IP address |
---|
| 1684 | + * @hw_dst: the DHCP server MAC address |
---|
| 1685 | + * @ip_dst: the DHCP server IP address |
---|
| 1686 | + * @vid: VLAN identifier |
---|
| 1687 | + * |
---|
| 1688 | + * Adds given MAC/IP pairs to the local DAT cache and propagates them further |
---|
| 1689 | + * into the DHT. |
---|
| 1690 | + * |
---|
| 1691 | + * For the DHT propagation, client MAC + IP will appear as the ARP Reply |
---|
| 1692 | + * transmitter (and hw_dst/ip_dst as the target). |
---|
| 1693 | + */ |
---|
| 1694 | +static void batadv_dat_put_dhcp(struct batadv_priv *bat_priv, u8 *chaddr, |
---|
| 1695 | + __be32 yiaddr, u8 *hw_dst, __be32 ip_dst, |
---|
| 1696 | + unsigned short vid) |
---|
| 1697 | +{ |
---|
| 1698 | + struct sk_buff *skb; |
---|
| 1699 | + |
---|
| 1700 | + skb = batadv_dat_arp_create_reply(bat_priv, yiaddr, ip_dst, chaddr, |
---|
| 1701 | + hw_dst, vid); |
---|
| 1702 | + if (!skb) |
---|
| 1703 | + return; |
---|
| 1704 | + |
---|
| 1705 | + skb_set_network_header(skb, ETH_HLEN); |
---|
| 1706 | + |
---|
| 1707 | + batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid); |
---|
| 1708 | + batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid); |
---|
| 1709 | + |
---|
| 1710 | + batadv_dat_forward_data(bat_priv, skb, yiaddr, vid, |
---|
| 1711 | + BATADV_P_DAT_DHT_PUT); |
---|
| 1712 | + batadv_dat_forward_data(bat_priv, skb, ip_dst, vid, |
---|
| 1713 | + BATADV_P_DAT_DHT_PUT); |
---|
| 1714 | + |
---|
| 1715 | + consume_skb(skb); |
---|
| 1716 | + |
---|
| 1717 | + batadv_dbg(BATADV_DBG_DAT, bat_priv, |
---|
| 1718 | + "Snooped from outgoing DHCPACK (server address): %pI4, %pM (vid: %i)\n", |
---|
| 1719 | + &ip_dst, hw_dst, batadv_print_vid(vid)); |
---|
| 1720 | + batadv_dbg(BATADV_DBG_DAT, bat_priv, |
---|
| 1721 | + "Snooped from outgoing DHCPACK (client address): %pI4, %pM (vid: %i)\n", |
---|
| 1722 | + &yiaddr, chaddr, batadv_print_vid(vid)); |
---|
| 1723 | +} |
---|
| 1724 | + |
---|
| 1725 | +/** |
---|
| 1726 | + * batadv_dat_check_dhcp_ack() - examine packet for valid DHCP message |
---|
| 1727 | + * @skb: the packet to check |
---|
| 1728 | + * @proto: ethernet protocol hint (behind a potential vlan) |
---|
| 1729 | + * @ip_src: a buffer to store the IPv4 source address in |
---|
| 1730 | + * @chaddr: a buffer to store the DHCP Client Hardware Address in |
---|
| 1731 | + * @yiaddr: a buffer to store the DHCP Your IP Address in |
---|
| 1732 | + * |
---|
| 1733 | + * Checks whether the given skb is a valid DHCPACK. And if so, stores the |
---|
| 1734 | + * IPv4 server source address (ip_src), client MAC address (chaddr) and client |
---|
| 1735 | + * IPv4 address (yiaddr) in the provided buffers. |
---|
| 1736 | + * |
---|
| 1737 | + * Caller needs to ensure that the skb network header is set correctly. |
---|
| 1738 | + * |
---|
| 1739 | + * Return: True if the skb is a valid DHCPACK. False otherwise. |
---|
| 1740 | + */ |
---|
| 1741 | +static bool |
---|
| 1742 | +batadv_dat_check_dhcp_ack(struct sk_buff *skb, __be16 proto, __be32 *ip_src, |
---|
| 1743 | + u8 *chaddr, __be32 *yiaddr) |
---|
| 1744 | +{ |
---|
| 1745 | + int type; |
---|
| 1746 | + |
---|
| 1747 | + type = batadv_dat_check_dhcp(skb, proto, ip_src); |
---|
| 1748 | + if (type != BATADV_BOOTREPLY) |
---|
| 1749 | + return false; |
---|
| 1750 | + |
---|
| 1751 | + type = batadv_dat_get_dhcp_message_type(skb); |
---|
| 1752 | + if (type != BATADV_DHCPACK) |
---|
| 1753 | + return false; |
---|
| 1754 | + |
---|
| 1755 | + if (!batadv_dat_dhcp_get_yiaddr(skb, yiaddr)) |
---|
| 1756 | + return false; |
---|
| 1757 | + |
---|
| 1758 | + if (!batadv_dat_get_dhcp_chaddr(skb, chaddr)) |
---|
| 1759 | + return false; |
---|
| 1760 | + |
---|
| 1761 | + return true; |
---|
| 1762 | +} |
---|
| 1763 | + |
---|
| 1764 | +/** |
---|
| 1765 | + * batadv_dat_snoop_outgoing_dhcp_ack() - snoop DHCPACK and fill DAT with it |
---|
| 1766 | + * @bat_priv: the bat priv with all the soft interface information |
---|
| 1767 | + * @skb: the packet to snoop |
---|
| 1768 | + * @proto: ethernet protocol hint (behind a potential vlan) |
---|
| 1769 | + * @vid: VLAN identifier |
---|
| 1770 | + * |
---|
| 1771 | + * This function first checks whether the given skb is a valid DHCPACK. If |
---|
| 1772 | + * so then its source MAC and IP as well as its DHCP Client Hardware Address |
---|
| 1773 | + * field and DHCP Your IP Address field are added to the local DAT cache and |
---|
| 1774 | + * propagated into the DHT. |
---|
| 1775 | + * |
---|
| 1776 | + * Caller needs to ensure that the skb mac and network headers are set |
---|
| 1777 | + * correctly. |
---|
| 1778 | + */ |
---|
| 1779 | +void batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv, |
---|
| 1780 | + struct sk_buff *skb, |
---|
| 1781 | + __be16 proto, |
---|
| 1782 | + unsigned short vid) |
---|
| 1783 | +{ |
---|
| 1784 | + u8 chaddr[BATADV_DHCP_CHADDR_LEN]; |
---|
| 1785 | + __be32 ip_src, yiaddr; |
---|
| 1786 | + |
---|
| 1787 | + if (!atomic_read(&bat_priv->distributed_arp_table)) |
---|
| 1788 | + return; |
---|
| 1789 | + |
---|
| 1790 | + if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr)) |
---|
| 1791 | + return; |
---|
| 1792 | + |
---|
| 1793 | + batadv_dat_put_dhcp(bat_priv, chaddr, yiaddr, eth_hdr(skb)->h_source, |
---|
| 1794 | + ip_src, vid); |
---|
| 1795 | +} |
---|
| 1796 | + |
---|
| 1797 | +/** |
---|
| 1798 | + * batadv_dat_snoop_incoming_dhcp_ack() - snoop DHCPACK and fill DAT cache |
---|
| 1799 | + * @bat_priv: the bat priv with all the soft interface information |
---|
| 1800 | + * @skb: the packet to snoop |
---|
| 1801 | + * @hdr_size: header size, up to the tail of the batman-adv header |
---|
| 1802 | + * |
---|
| 1803 | + * This function first checks whether the given skb is a valid DHCPACK. If |
---|
| 1804 | + * so then its source MAC and IP as well as its DHCP Client Hardware Address |
---|
| 1805 | + * field and DHCP Your IP Address field are added to the local DAT cache. |
---|
| 1806 | + */ |
---|
| 1807 | +void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv, |
---|
| 1808 | + struct sk_buff *skb, int hdr_size) |
---|
| 1809 | +{ |
---|
| 1810 | + u8 chaddr[BATADV_DHCP_CHADDR_LEN]; |
---|
| 1811 | + struct ethhdr *ethhdr; |
---|
| 1812 | + __be32 ip_src, yiaddr; |
---|
| 1813 | + unsigned short vid; |
---|
| 1814 | + __be16 proto; |
---|
| 1815 | + u8 *hw_src; |
---|
| 1816 | + |
---|
| 1817 | + if (!atomic_read(&bat_priv->distributed_arp_table)) |
---|
| 1818 | + return; |
---|
| 1819 | + |
---|
| 1820 | + if (unlikely(!pskb_may_pull(skb, hdr_size + ETH_HLEN))) |
---|
| 1821 | + return; |
---|
| 1822 | + |
---|
| 1823 | + ethhdr = (struct ethhdr *)(skb->data + hdr_size); |
---|
| 1824 | + skb_set_network_header(skb, hdr_size + ETH_HLEN); |
---|
| 1825 | + proto = ethhdr->h_proto; |
---|
| 1826 | + |
---|
| 1827 | + if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr)) |
---|
| 1828 | + return; |
---|
| 1829 | + |
---|
| 1830 | + hw_src = ethhdr->h_source; |
---|
| 1831 | + vid = batadv_dat_get_vid(skb, &hdr_size); |
---|
| 1832 | + |
---|
| 1833 | + batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid); |
---|
| 1834 | + batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); |
---|
| 1835 | + |
---|
| 1836 | + batadv_dbg(BATADV_DBG_DAT, bat_priv, |
---|
| 1837 | + "Snooped from incoming DHCPACK (server address): %pI4, %pM (vid: %i)\n", |
---|
| 1838 | + &ip_src, hw_src, batadv_print_vid(vid)); |
---|
| 1839 | + batadv_dbg(BATADV_DBG_DAT, bat_priv, |
---|
| 1840 | + "Snooped from incoming DHCPACK (client address): %pI4, %pM (vid: %i)\n", |
---|
| 1841 | + &yiaddr, chaddr, batadv_print_vid(vid)); |
---|
| 1842 | +} |
---|
| 1843 | + |
---|
| 1844 | +/** |
---|
1443 | 1845 | * batadv_dat_drop_broadcast_packet() - check if an ARP request has to be |
---|
1444 | 1846 | * dropped (because the node has already obtained the reply via DAT) or not |
---|
1445 | 1847 | * @bat_priv: the bat priv with all the soft interface information |
---|