1/*
2 * Copyright (c) 2007-12 ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <sys/endian.h>
11#include <barrelfish/barrelfish.h>
12#include <barrelfish/waitset.h>
13#include <barrelfish/nameservice_client.h>
14#include <barrelfish/net_constants.h>
15#include <barrelfish/bulk_transfer.h>
16//#include <net_device_manager/net_ports_service.h>
17#include <if/net_soft_filters_defs.h>
18#include <if/net_soft_filters_defs.h>
19//#include <if/net_ports_defs.h>
20
21// For filter generation
22#include <bfdmuxtools/tools.h>
23#include <bfdmuxtools/codegen.h>
24
25#include <stdio.h>
26#include <string.h>
27#include <arpa/inet.h>
28
29#include "port_management_support.h"
30#include "device_manager_debug.h"
31
32
33#define NORMAL_FILTER       (1)
34#define REDIRECT_FILTER      (2)
35
36/****************************************************************
37* Local states
38*****************************************************************/
39
40// handle for connection with soft filters service.
41static struct net_soft_filters_binding *soft_filters_connection = NULL;
42
43static bool soft_filters_ready = false;
44// bulk_transfer used to move the packet filters between net_device_manager
45// and queue_manager
46static struct bulk_transfer bt_filter_tx;
47
48static bool filter_mem_lock = true; // protects the above filter memory
49
50
51// Local ip address assigned to the interface
52struct ip_addr local_ip = {
53        .addr = BFDMUX_IP_ADDR_ANY
54    };
55
56static bool valid_mac_addr_assigned = false; // marks valid mac address
57static struct eth_addr mac; // = { .addr = {0, 0, 0, 0, 0, 0}};
58
59// *****************************************************************
60// * related to managing soft filters
61// *****************************************************************
62
63static uint64_t populate_rx_tx_filter_mem(uint16_t port,
64        net_ports_port_type_t type, int32_t *len_rx, int32_t *len_tx);
65
66static void connect_soft_filters_service(char *dev_name, qid_t qid);
67static void register_arp_soft_filter(uint64_t id, uint64_t len_rx,
68                                    uint64_t len_tx);
69
70static errval_t register_soft_filt_impl(uint16_t port,
71                    port_type_t type,
72                    bufid_t buffer_id_rx,
73                    bufid_t buffer_id_tx,
74                    appid_t appid,
75                    qid_t qid,
76                    uint64_t *id, errval_t *rerr, uint64_t *filter_id);
77
78static errval_t unregister_soft_filter(uint64_t filter_id, qid_t qid);
79
80static struct filters_tx_vtbl soft_filts_mng = {
81    .type = "Soft_filters",
82    .init_filters = connect_soft_filters_service,
83    .reg_arp_filters = register_arp_soft_filter,
84    .reg_filters = register_soft_filt_impl,
85    .unreg_filters = unregister_soft_filter,
86};
87
88/*****************************************************************
89* Prototypes
90*****************************************************************/
91static void share_common_memory_with_filter_manager(void);
92static void sf_mac_lookup(void);
93
94static void send_soft_filter(uint64_t id, uint64_t len_rx, uint64_t len_tx,
95                                uint64_t buffer_id_rx, uint64_t buffer_id_tx,
96                                uint8_t ftype, uint8_t paused,
97                                errval_t *rerr, uint64_t *filter_id);
98
99// *****************************************************************
100// * Get signature of this service
101// *****************************************************************
102struct filters_tx_vtbl *get_soft_filt_mng_sign(void)
103{
104    return &soft_filts_mng;
105} // end function: get_filt_mng_sign
106
107
108// *****************************************************************
109// * converting port requests into filters
110// *****************************************************************
111
112
113static errval_t register_soft_filt_impl(uint16_t port,
114                    port_type_t type,
115                    bufid_t buffer_id_rx,
116                    bufid_t buffer_id_tx,
117                    appid_t appid,
118                    qid_t qid,
119                    uint64_t *id, errval_t *rerr, uint64_t *filter_id)
120{
121    int32_t len_rx, len_tx;
122    /* NOTE: check if someone else is using the filter location */
123    if (filter_mem_lock) {
124        /* FIXME: as there is only one registered location for filter
125           transfer, only one filter registration can be done at one time. */
126        NDM_DEBUG("filter memory is busy.\n");
127        return FILTER_ERR_FILTER_BUSY;
128    }
129
130    /* create rx, tx filter around that port */
131    filter_mem_lock = true;     /* NOTE: filter memory is in use
132                                   till "registered_filter" is called by filter_manager */
133    *id = populate_rx_tx_filter_mem(port, type, &len_rx, &len_tx);
134
135    /* Register the filter with soft_filters */
136    NDM_DEBUG("get_port: trying to register the filter with id %" PRIu64 "\n",
137               *id);
138    send_soft_filter(*id, len_rx, len_tx, buffer_id_rx, buffer_id_tx,
139                        NORMAL_FILTER, 0, rerr, filter_id);
140
141    return SYS_ERR_OK;
142}
143
144
145// *****************************************************************
146// * Connect with soft filter service
147// *****************************************************************
148
149static void soft_filters_bind_cb(void *st, errval_t err,
150                                  struct net_soft_filters_binding *enb)
151{
152    if (err_is_fail(err)) {
153        DEBUG_ERR(err, "bind failed");
154        abort();
155    }
156    NDM_DEBUG("soft_filters_bind_cb: started\n");
157
158    net_soft_filters_rpc_client_init(enb);
159
160    soft_filters_connection = enb;
161    NDM_DEBUG(" soft_filters_bind_cb: connection made,"
162               " now registering memory \n");
163    NDM_DEBUG("soft_filters_bind_cb: terminated\n");
164}
165
166
167// \brief: Establishes connection with soft_filters service
168static void connect_soft_filters_service(char *dev_name, qid_t qid)
169{
170    assert(dev_name != NULL);
171    NDM_DEBUG("c_sf_mng: connecting to dev [%s]\n", dev_name);
172
173    errval_t err;
174
175    // The service name
176    char service_name[MAX_NET_SERVICE_NAME_LEN];
177
178    snprintf(service_name, sizeof(service_name), "%s_%" PRIu64 "%s",
179            dev_name, qid, FILTER_SERVICE_SUFFIX);
180    NDM_DEBUG("c_sf_mng: resolving service [%s]\n", service_name);
181
182    // locate the service
183    iref_t iref = 0;
184    err = nameservice_blocking_lookup(service_name, &iref);
185    if (err_is_fail(err)) {
186        DEBUG_ERR(err, "c_sf_mng: could not connect to soft filter manager .\n"
187                  "Terminating.\n");
188        abort();
189    }
190    assert(iref != 0);
191
192    // Connect to the service
193    NDM_DEBUG("c_sf_mng: connecting\n");
194
195    err = net_soft_filters_bind(iref, soft_filters_bind_cb, NULL,
196                         get_default_waitset(), IDC_BIND_FLAGS_DEFAULT);
197    assert(err_is_ok(err));
198
199    // waiting for connection to succeed.
200    NDM_DEBUG("connect_to_ether_filter_manager: wait connection\n");
201    while (soft_filters_connection == NULL) {
202        messages_wait_and_handle_next();
203    }
204
205    // providing buffers for sending soft_filters
206    NDM_DEBUG("c_sf_mng: [%s] sharing memory\n", service_name);
207    share_common_memory_with_filter_manager();
208
209    NDM_DEBUG("c_sf_mng: [%s] sharing memory\n", service_name);
210
211    sf_mac_lookup();
212
213    printf("################################******\n");
214    printf("For service [%s] MAC= %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
215                        service_name,  mac.addr[0], mac.addr[1], mac.addr[2],
216                        mac.addr[3], mac.addr[4], mac.addr[5]);
217
218} // end function: connect_soft_filters_manager
219
220
221// *****************************************************************
222// * filter memory registration
223// * One time process
224// *****************************************************************
225/**
226 * \brief sends cap to the memory which is to be shared between filter_manager
227 *   of network driver and netd.
228 *
229 */
230static void register_filter_memory(struct capref cap)
231{
232    struct net_soft_filters_binding *b = soft_filters_connection;
233
234    errval_t err, rerr;
235    err = b->rpc_tx_vtbl.register_filter_memory(b, cap, &rerr);
236    assert(err_is_ok(err));
237    assert(err_is_ok(rerr));
238    soft_filters_ready = true;
239}
240
241
242/**
243* \brief: share the memory so that filter passing can be started.
244*/
245static void share_common_memory_with_filter_manager(void)
246{
247    errval_t err;
248    struct capref frame;
249    size_t size = BASE_PAGE_SIZE * 2;
250    size_t total_size = size * 7;
251
252    NDM_DEBUG("SCMWFM: started\n");
253
254    NDM_DEBUG("SCMWFM: allocating %lu bytes of memory.\n", size);
255
256    err = bulk_create(total_size, size, &frame, &bt_filter_tx);
257
258    assert(err_is_ok(err));
259
260    NDM_DEBUG("SCMWFM: registering netd filter memory\n");
261    register_filter_memory(frame);
262    NDM_DEBUG("SCMWFM: terminated\n");
263
264    // waiting for connection to succeed.
265    NDM_DEBUG("connect_to_ether_filter_manager: wait connection\n");
266    while (!soft_filters_ready) {
267        messages_wait_and_handle_next();
268    }
269
270    filter_mem_lock = false; // marking memory as ready to use
271} // end function: share_common_memory_with_filter_manager
272
273// Support code to convert mac address from uint64_t into eth_addr type
274union mac_addr_un1 {
275    struct eth_addr ethaddr;
276    uint64_t mac_addr;
277};
278
279static struct eth_addr my_convert_uint64_to_eth_addr(uint64_t given_mac)
280{
281    union mac_addr_un1 tmp_mac;
282    tmp_mac.mac_addr = given_mac;
283
284    // FIXME: make sure that this works irrespective of endianness of a machine
285    return tmp_mac.ethaddr;
286}
287
288// lookup the mac address
289static void sf_mac_lookup(void)
290{
291    struct net_soft_filters_binding *b = soft_filters_connection;
292
293    uint64_t mac_addr;
294    errval_t err, rerr;
295    err = b->rpc_tx_vtbl.mac_address(b, &rerr, &mac_addr);
296    assert(err_is_ok(err));
297    assert(err_is_ok(rerr));
298    mac = my_convert_uint64_to_eth_addr(mac_addr);
299    valid_mac_addr_assigned = true;
300} // end function: sf_mac_lookup
301
302/**
303 * \brief sends the filter for registration to network driver.
304 *
305 */
306static void send_soft_filter(uint64_t id, uint64_t len_rx,
307                                uint64_t len_tx, uint64_t buffer_id_rx,
308                                uint64_t buffer_id_tx, uint8_t ftype,
309                                uint8_t paused,
310                                errval_t *rerr, uint64_t *filter_id)
311{
312    NDM_DEBUG("send_soft_filter: called for id %" PRIu64
313               " and type %x, paused = %d\n", id, ftype, paused);
314
315    struct net_soft_filters_binding *b = soft_filters_connection;
316
317    errval_t err;
318    err = b->rpc_tx_vtbl.register_filter(b, id, len_rx, len_tx, buffer_id_rx, buffer_id_tx, ftype, paused, rerr, filter_id);
319    assert(err_is_ok(err));
320    assert(err_is_ok(*rerr));
321    NDM_DEBUG("filter at id [%" PRIu64 "] type[%" PRIu64
322               "] registered with filt_id %" PRIu64 ".\n", id, ftype,
323               filter_id);
324
325    /* free the memory in shared area */
326    err = bulk_free(&bt_filter_tx, id);
327    assert(err_is_ok(err));
328
329    filter_mem_lock = false; // NOTE: filter memory can be used by others now
330} // end function: send_soft_filter
331
332/**
333 * \brief sends the filterID for de-registration to network driver.
334 *
335 */
336static errval_t unregister_soft_filter(uint64_t filter_id, qid_t qid)
337{
338    struct net_soft_filters_binding *b = soft_filters_connection;
339
340    errval_t err, rerr;
341    err = b->rpc_tx_vtbl.deregister_filter(b, filter_id, &rerr);
342    assert(err_is_ok(err));
343    assert(err_is_ok(rerr));
344    return rerr;
345} // end function: unregister_soft_filter
346
347/**
348 * \brief sends the filter for registration to network driver.
349 *
350 */
351static void register_arp_soft_filter(uint64_t id, uint64_t len_rx,
352                                    uint64_t len_tx)
353{
354    NDM_DEBUG("register_arp_soft_filter: called\n");
355    struct net_soft_filters_binding *b = soft_filters_connection;
356
357    errval_t err, rerr;
358    err = b->rpc_tx_vtbl.register_arp_filter(b, id, len_rx, len_tx, &rerr);
359    assert(err_is_ok(err));
360    assert(err_is_ok(rerr));
361
362} // end function: register_arp_soft_filter
363
364static uint64_t populate_rx_tx_filter_mem(uint16_t port, net_ports_port_type_t type,
365                                          int32_t * len_rx, int32_t * len_tx)
366{
367
368    struct bulk_buf *bb;
369
370    /* get memory chunk from shared memory */
371    do {
372        bb = bulk_alloc(&bt_filter_tx);
373        if (bb == NULL) {
374            // dispatch one
375            NDM_DEBUG("bulk_alloc is returning NULL!!!! for filter memory\n");
376            event_dispatch(get_default_waitset());
377        }
378    } while (bb == NULL);
379    void *bbuf = bulk_buf_get_mem(bb);
380
381
382    uint8_t *filter_mem = NULL;
383    char *filter;
384
385
386    // rx filter
387    if (type == net_ports_PORT_TCP) {
388        filter = build_ether_dst_ipv4_tcp_filter(mac,
389                                BFDMUX_IP_ADDR_ANY,
390                                htonl(local_ip.addr),
391                                PORT_ANY,
392                                (port_t) port
393                                );
394    } else {
395        filter = build_ether_dst_ipv4_udp_filter(mac,
396                                BFDMUX_IP_ADDR_ANY,
397                                htonl(local_ip.addr),
398                                PORT_ANY,
399                                (port_t) port
400                                );
401    }
402    /* FIXME: shouldn't be above two ports be wrapped in htons(port)? */
403//    printf("##### The created filter is [%s]\n", filter);
404    compile_filter(filter, &filter_mem, len_rx);
405    assert(*len_rx < BASE_PAGE_SIZE);
406
407    assert(filter_mem != NULL);
408    memcpy(bbuf, filter_mem, *len_rx);
409    free(filter);
410    free(filter_mem);
411
412    // tx filter
413    if (type == net_ports_PORT_TCP) {
414        filter = build_ether_src_ipv4_tcp_filter(mac, htonl(local_ip.addr),
415                                                 BFDMUX_IP_ADDR_ANY,
416                                                 (port_t) port, PORT_ANY);
417    } else {
418        filter = build_ether_src_ipv4_udp_filter(mac, htonl(local_ip.addr),
419                                                 BFDMUX_IP_ADDR_ANY,
420                                                 (port_t) port, PORT_ANY);
421    }
422    compile_filter(filter, &filter_mem, len_tx);
423    assert(*len_tx < BASE_PAGE_SIZE);
424
425    void *bbuf_tx = bbuf + BASE_PAGE_SIZE;
426
427    memcpy(bbuf_tx, filter_mem, *len_tx);
428
429    free(filter);
430    free(filter_mem);
431    uint64_t id = bulk_prepare_send(bb);
432
433    return id;
434}
435