linux/net/batman-adv/unicast.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2010-2011 B.A.T.M.A.N. contributors:
   3 *
   4 * Andreas Langer
   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, write to the Free Software
  17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  18 * 02110-1301, USA
  19 *
  20 */
  21
  22#include "main.h"
  23#include "unicast.h"
  24#include "send.h"
  25#include "soft-interface.h"
  26#include "gateway_client.h"
  27#include "originator.h"
  28#include "hash.h"
  29#include "translation-table.h"
  30#include "routing.h"
  31#include "hard-interface.h"
  32
  33
  34static struct sk_buff *frag_merge_packet(struct list_head *head,
  35                                         struct frag_packet_list_entry *tfp,
  36                                         struct sk_buff *skb)
  37{
  38        struct unicast_frag_packet *up =
  39                (struct unicast_frag_packet *)skb->data;
  40        struct sk_buff *tmp_skb;
  41        struct unicast_packet *unicast_packet;
  42        int hdr_len = sizeof(*unicast_packet);
  43        int uni_diff = sizeof(*up) - hdr_len;
  44
  45        /* set skb to the first part and tmp_skb to the second part */
  46        if (up->flags & UNI_FRAG_HEAD) {
  47                tmp_skb = tfp->skb;
  48        } else {
  49                tmp_skb = skb;
  50                skb = tfp->skb;
  51        }
  52
  53        if (skb_linearize(skb) < 0 || skb_linearize(tmp_skb) < 0)
  54                goto err;
  55
  56        skb_pull(tmp_skb, sizeof(*up));
  57        if (pskb_expand_head(skb, 0, tmp_skb->len, GFP_ATOMIC) < 0)
  58                goto err;
  59
  60        /* move free entry to end */
  61        tfp->skb = NULL;
  62        tfp->seqno = 0;
  63        list_move_tail(&tfp->list, head);
  64
  65        memcpy(skb_put(skb, tmp_skb->len), tmp_skb->data, tmp_skb->len);
  66        kfree_skb(tmp_skb);
  67
  68        memmove(skb->data + uni_diff, skb->data, hdr_len);
  69        unicast_packet = (struct unicast_packet *) skb_pull(skb, uni_diff);
  70        unicast_packet->packet_type = BAT_UNICAST;
  71
  72        return skb;
  73
  74err:
  75        /* free buffered skb, skb will be freed later */
  76        kfree_skb(tfp->skb);
  77        return NULL;
  78}
  79
  80static void frag_create_entry(struct list_head *head, struct sk_buff *skb)
  81{
  82        struct frag_packet_list_entry *tfp;
  83        struct unicast_frag_packet *up =
  84                (struct unicast_frag_packet *)skb->data;
  85
  86        /* free and oldest packets stand at the end */
  87        tfp = list_entry((head)->prev, typeof(*tfp), list);
  88        kfree_skb(tfp->skb);
  89
  90        tfp->seqno = ntohs(up->seqno);
  91        tfp->skb = skb;
  92        list_move(&tfp->list, head);
  93        return;
  94}
  95
  96static int frag_create_buffer(struct list_head *head)
  97{
  98        int i;
  99        struct frag_packet_list_entry *tfp;
 100
 101        for (i = 0; i < FRAG_BUFFER_SIZE; i++) {
 102                tfp = kmalloc(sizeof(*tfp), GFP_ATOMIC);
 103                if (!tfp) {
 104                        frag_list_free(head);
 105                        return -ENOMEM;
 106                }
 107                tfp->skb = NULL;
 108                tfp->seqno = 0;
 109                INIT_LIST_HEAD(&tfp->list);
 110                list_add(&tfp->list, head);
 111        }
 112
 113        return 0;
 114}
 115
 116static struct frag_packet_list_entry *frag_search_packet(struct list_head *head,
 117                                           const struct unicast_frag_packet *up)
 118{
 119        struct frag_packet_list_entry *tfp;
 120        struct unicast_frag_packet *tmp_up = NULL;
 121        uint16_t search_seqno;
 122
 123        if (up->flags & UNI_FRAG_HEAD)
 124                search_seqno = ntohs(up->seqno)+1;
 125        else
 126                search_seqno = ntohs(up->seqno)-1;
 127
 128        list_for_each_entry(tfp, head, list) {
 129
 130                if (!tfp->skb)
 131                        continue;
 132
 133                if (tfp->seqno == ntohs(up->seqno))
 134                        goto mov_tail;
 135
 136                tmp_up = (struct unicast_frag_packet *)tfp->skb->data;
 137
 138                if (tfp->seqno == search_seqno) {
 139
 140                        if ((tmp_up->flags & UNI_FRAG_HEAD) !=
 141                            (up->flags & UNI_FRAG_HEAD))
 142                                return tfp;
 143                        else
 144                                goto mov_tail;
 145                }
 146        }
 147        return NULL;
 148
 149mov_tail:
 150        list_move_tail(&tfp->list, head);
 151        return NULL;
 152}
 153
 154void frag_list_free(struct list_head *head)
 155{
 156        struct frag_packet_list_entry *pf, *tmp_pf;
 157
 158        if (!list_empty(head)) {
 159
 160                list_for_each_entry_safe(pf, tmp_pf, head, list) {
 161                        kfree_skb(pf->skb);
 162                        list_del(&pf->list);
 163                        kfree(pf);
 164                }
 165        }
 166        return;
 167}
 168
 169/* frag_reassemble_skb():
 170 * returns NET_RX_DROP if the operation failed - skb is left intact
 171 * returns NET_RX_SUCCESS if the fragment was buffered (skb_new will be NULL)
 172 * or the skb could be reassembled (skb_new will point to the new packet and
 173 * skb was freed)
 174 */
 175int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
 176                        struct sk_buff **new_skb)
 177{
 178        struct orig_node *orig_node;
 179        struct frag_packet_list_entry *tmp_frag_entry;
 180        int ret = NET_RX_DROP;
 181        struct unicast_frag_packet *unicast_packet =
 182                (struct unicast_frag_packet *)skb->data;
 183
 184        *new_skb = NULL;
 185
 186        orig_node = orig_hash_find(bat_priv, unicast_packet->orig);
 187        if (!orig_node)
 188                goto out;
 189
 190        orig_node->last_frag_packet = jiffies;
 191
 192        if (list_empty(&orig_node->frag_list) &&
 193            frag_create_buffer(&orig_node->frag_list)) {
 194                pr_debug("couldn't create frag buffer\n");
 195                goto out;
 196        }
 197
 198        tmp_frag_entry = frag_search_packet(&orig_node->frag_list,
 199                                            unicast_packet);
 200
 201        if (!tmp_frag_entry) {
 202                frag_create_entry(&orig_node->frag_list, skb);
 203                ret = NET_RX_SUCCESS;
 204                goto out;
 205        }
 206
 207        *new_skb = frag_merge_packet(&orig_node->frag_list, tmp_frag_entry,
 208                                     skb);
 209        /* if not, merge failed */
 210        if (*new_skb)
 211                ret = NET_RX_SUCCESS;
 212
 213out:
 214        if (orig_node)
 215                orig_node_free_ref(orig_node);
 216        return ret;
 217}
 218
 219int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
 220                  struct hard_iface *hard_iface, const uint8_t dstaddr[])
 221{
 222        struct unicast_packet tmp_uc, *unicast_packet;
 223        struct hard_iface *primary_if;
 224        struct sk_buff *frag_skb;
 225        struct unicast_frag_packet *frag1, *frag2;
 226        int uc_hdr_len = sizeof(*unicast_packet);
 227        int ucf_hdr_len = sizeof(*frag1);
 228        int data_len = skb->len - uc_hdr_len;
 229        int large_tail = 0, ret = NET_RX_DROP;
 230        uint16_t seqno;
 231
 232        primary_if = primary_if_get_selected(bat_priv);
 233        if (!primary_if)
 234                goto dropped;
 235
 236        frag_skb = dev_alloc_skb(data_len - (data_len / 2) + ucf_hdr_len);
 237        if (!frag_skb)
 238                goto dropped;
 239        skb_reserve(frag_skb, ucf_hdr_len);
 240
 241        unicast_packet = (struct unicast_packet *) skb->data;
 242        memcpy(&tmp_uc, unicast_packet, uc_hdr_len);
 243        skb_split(skb, frag_skb, data_len / 2 + uc_hdr_len);
 244
 245        if (my_skb_head_push(skb, ucf_hdr_len - uc_hdr_len) < 0 ||
 246            my_skb_head_push(frag_skb, ucf_hdr_len) < 0)
 247                goto drop_frag;
 248
 249        frag1 = (struct unicast_frag_packet *)skb->data;
 250        frag2 = (struct unicast_frag_packet *)frag_skb->data;
 251
 252        memcpy(frag1, &tmp_uc, sizeof(tmp_uc));
 253
 254        frag1->ttl--;
 255        frag1->version = COMPAT_VERSION;
 256        frag1->packet_type = BAT_UNICAST_FRAG;
 257
 258        memcpy(frag1->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
 259        memcpy(frag2, frag1, sizeof(*frag2));
 260
 261        if (data_len & 1)
 262                large_tail = UNI_FRAG_LARGETAIL;
 263
 264        frag1->flags = UNI_FRAG_HEAD | large_tail;
 265        frag2->flags = large_tail;
 266
 267        seqno = atomic_add_return(2, &hard_iface->frag_seqno);
 268        frag1->seqno = htons(seqno - 1);
 269        frag2->seqno = htons(seqno);
 270
 271        send_skb_packet(skb, hard_iface, dstaddr);
 272        send_skb_packet(frag_skb, hard_iface, dstaddr);
 273        ret = NET_RX_SUCCESS;
 274        goto out;
 275
 276drop_frag:
 277        kfree_skb(frag_skb);
 278dropped:
 279        kfree_skb(skb);
 280out:
 281        if (primary_if)
 282                hardif_free_ref(primary_if);
 283        return ret;
 284}
 285
 286int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv)
 287{
 288        struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
 289        struct unicast_packet *unicast_packet;
 290        struct orig_node *orig_node;
 291        struct neigh_node *neigh_node;
 292        int data_len = skb->len;
 293        int ret = 1;
 294
 295        /* get routing information */
 296        if (is_multicast_ether_addr(ethhdr->h_dest)) {
 297                orig_node = gw_get_selected_orig(bat_priv);
 298                if (orig_node)
 299                        goto find_router;
 300        }
 301
 302        /* check for tt host - increases orig_node refcount.
 303         * returns NULL in case of AP isolation */
 304        orig_node = transtable_search(bat_priv, ethhdr->h_source,
 305                                      ethhdr->h_dest);
 306
 307find_router:
 308        /**
 309         * find_router():
 310         *  - if orig_node is NULL it returns NULL
 311         *  - increases neigh_nodes refcount if found.
 312         */
 313        neigh_node = find_router(bat_priv, orig_node, NULL);
 314
 315        if (!neigh_node)
 316                goto out;
 317
 318        if (my_skb_head_push(skb, sizeof(*unicast_packet)) < 0)
 319                goto out;
 320
 321        unicast_packet = (struct unicast_packet *)skb->data;
 322
 323        unicast_packet->version = COMPAT_VERSION;
 324        /* batman packet type: unicast */
 325        unicast_packet->packet_type = BAT_UNICAST;
 326        /* set unicast ttl */
 327        unicast_packet->ttl = TTL;
 328        /* copy the destination for faster routing */
 329        memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN);
 330        /* set the destination tt version number */
 331        unicast_packet->ttvn =
 332                (uint8_t)atomic_read(&orig_node->last_ttvn);
 333
 334        if (atomic_read(&bat_priv->fragmentation) &&
 335            data_len + sizeof(*unicast_packet) >
 336                                neigh_node->if_incoming->net_dev->mtu) {
 337                /* send frag skb decreases ttl */
 338                unicast_packet->ttl++;
 339                ret = frag_send_skb(skb, bat_priv,
 340                                    neigh_node->if_incoming, neigh_node->addr);
 341                goto out;
 342        }
 343
 344        send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
 345        ret = 0;
 346        goto out;
 347
 348out:
 349        if (neigh_node)
 350                neigh_node_free_ref(neigh_node);
 351        if (orig_node)
 352                orig_node_free_ref(orig_node);
 353        if (ret == 1)
 354                kfree_skb(skb);
 355        return ret;
 356}
 357
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.