Writing a Dynamic Firewall Filter

For an introduction to the dynamic firewall filter functionality, see Dynamic Firewall Filters.

The API for dynamic firewall filters is defined in src/junos/lib/libdfwd in your backing sandbox, and is documented in the SDK Library reference.

Basic Tasks

There are two different things that an application writer must do to add firewall filters:

The filter becomes operational only when it is attached to an interface: that is, incoming and outgoing packets through that interface pass through the filter.

You begin the filter definition by calling junos_dfw_filter_trans_alloc(), identifying the filter by setting values for namestr_key and owner_client_id in the argument filter_info.

To specify a new filter definition or to change a previously configured filter, you use the operation JUNOS_DFW_FILTER_OP_ADD or JUNOS_DFW_FILTER_OP_CHANGE. Multiple terms can be configured in a filter using the handle obtained for these operations.

To configure a term:

  1. Call junos_dfw_term_start().

  2. Optionally, call Match functions to specify which terms the filter should match. If you do not specify at least one match condition, the system matches all traffic.

  3. Optionally, call Action functions to specify the action to take if the packet matches the conditions you have configured in the term. If you specify no actions, the system takes the default action for the filter type.

  4. Call junos_dfw_term_end().

The junos_dfw_filter_types_t structure determines the type of filter, either classic or fast update. For introductory information about these filter types, see Dynamic Firewall Filters.

The junos_dfw_filter_op_t structure indicates the configuration operation to be executed.

Limitations on Fast Update Filters

The following are not supported for fast update filters:

For a complete list of supported function calls for each type of firewall filter, see the documentation for libdfwd in the SDK Library reference.

Sample Code: Classic Dynamic Firewall Filter

The following example uses a single match condition to accept or deny packets from a given address. The same principle applies for multiple conditions.

    /*
     * Globals assumed already set up for example, or they could be
     * parameters passed to the called function.
     */
    junos_dfw_session_handle_t session;
    junos_dfw_client_id_t client_id;
    u_int64_t ctx;


    int
    acme_accept_mac_addresses (uint8_t *mac_addr_p, uint8_t
        mac_prefix_len)
    {

        int error;
        junos_dfw_trans_handle_t trans_hdl;
        junos_dfw_filter_info_t filter_info;
        junos_dfw_term_info_t term_info;


        /* Do some basic checks. */
        if (!mac_addr_p || !mac_prefix_len) {
            acme_log("%s():%d - Bad input parameters!\n", __func__,
                __LINE__);
            return -1;
        }


        /* Initialize the filter-info structure. */
        filter_info.namestr_key = "acme-filter-macs";
        filter_info.type = JUNOS_DFW_FILTER_TYPE_CLASSIC;
        filter_info.addr_family = JUNOS_DFW_FILTER_AF_INET;


        /* Get a transaction handle. */
        error =
            junos_dfw_filter_trans_alloc(&filter_info,
                JUNOS_DFW_FILTER_OP_ADD, &trans_hdl);

        if (error) {
            /* Couldn't get a transaction handle ... leave! */
            acme_log("%s:%d() - junos_dfw_filter_trans_alloc failed!\n",
                __func__, __LINE__);
            return error;
        }


        /* Define the term. */
        term_info.namestr_key = "acme-macs-term";

        /* Classic filters require terms to be ordered. */
        term_info.type = JUNOS_DFW_TERM_TYPE_ORDERED;
        term_info.property.order.term_adj_type =
            JUNOS_DFW_TERM_ADJ_PREV;
        term_info.property.order.term_adj_namestr_key = NULL;

        /* Start with the term ... */
        error =
            junos_dfw_term_start(trans_hdl, &term_info,
                JUNOS_DFW_TERM_OP_ADD);

        if (error) {
            /* Couldn't start making a term ... leave! */
            acme_log("%s:%d() - junos_dfw_term_start failed!\n",
                __func__, __LINE__);
            (void) junos_dfw_trans_handle_free(trans_hdl);
            return error;
        }


        /* Include a match condition. */
        error =
            junos_dfw_term_match_dest_mac(trans_hdl, mac_addr_p,
                mac_prefix_len, JUNOS_DFW_FILTER_OP_MATCH);

        if (error) {
            /* Couldn't create a match condition ... leave! */
            acme_log("%s:%d() - junos_dfw_term_match_src_mac failed!\n",
                __func__, __LINE__);
            (void) junos_dfw_trans_handle_free(trans_hdl);
            return error;
        }


        /* Specify an action. */
        error = junos_dfw_term_action_accept(trans_hdl);
        if (error) {
            /* Couldn't specify an action ... leave! */
            acme_log("%s:%d() - junos_dfw_term_action_accept failed!\n",
                __func__, __LINE__);
            (void) junos_dfw_trans_handle_free(trans_hdl);
            return error;
        }


        /* Close the term. */
        error = junos_dfw_term_end(trans_hdl);
        if (error) {
            /* Couldn't close term ... leave! */
            acme_log("%s:%d() - junos_dfw_term_end failed!\n",
                __func__, __LINE__);
            (void) junos_dfw_trans_handle_free(trans_hdl);
            return error;
        }


        /* Send the transaction. */
        error =
            junos_dfw_trans_send(session, trans_hdl, client_id,
                user_ctx);

        if (error) {
            /* Couldn't send the transaction ... leave! */
            acme_log("%s:%d() - junos_dfw_trans_send failed!\n",
                __func__, __LINE__);
            (void) junos_dfw_trans_handle_free(trans_hdl);
            return error;
        }


        /* We're done. */
        return 0;
    }

At this point, you can apply the filter you have just configured to the input side of an interface (see Sample Code: Applying a Filter to an Interface).

You can use the same logic for other Layer 2 functions, substituting their respective set of parameters.

Sample Code: Fast-Update Dynamic Firewall Filter

The following code configures a policer for voice traffic.

/* 
     * Configure policers
     */
    junos_dfw_trans_handle_t policer_trans_hdl;
    junos_dfw_policer_info_t policer_info;
    
    /*
     * First configure policer "voice-traffic"
     */
    policer_info.namestr_key = "voice-traffic";
    
    if (junos_dfw_policer_trans_alloc(&policer_info,
                JUNOS_DFW_POLICER_OP_ADD, /*operation*/
                &policer_trans_hdl) < 0) {
        perror("junos_dfw_policer_trans_alloc failed\n");
    } else if (junos_dfw_policer_parameters(policer_trans_hdl,
                300, /*rate*/
                JUNOS_DFW_RATE_UNIT_KBPS, /*rate_unit*/
                3, /*burst_size_limit_bytes*/
                JUNOS_DFW_BURST_SIZE_UNIT_KBYTE,
                FALSE) < 0) /*filter_specific*/ {
        perror("junos_dfw_policer_parameters failed\n");
    } else if (junos_dfw_policer_action_discard(policer_trans_hdl)  < 0) {
        perror("junos_dfw_policer_action_discard failed\n");
    } else if (junos_dfw_trans_tx(session, policer_trans_hdl,
                id, user_ctx) < 0)  {
        perror("junos_dfw_trans_tx failed\n");
    } 
    
    /* 
     * Specify filter information. 
     */
    junos_dfw_filter_info_t  filter_info;
    
    /*
     * Initialize filter info struct to be used for transaction
     * allocation.
     */
    filter_info.namestr_key = "media-service";
    filter_info.type = JUNOS_DFW_FILTER_TYPE_FAST_UPDATE;
    filter_info.addr_family = JUNOS_DFW_FILTER_AF_INET;
    
    /*
     * Allocate a filter config transaction handle.
     */
    junos_dfw_trans_handle_t trans_hdl;
    
    if (junos_dfw_filter_trans_alloc(&filter_info,
                JUNOS_DFW_FILTER_OP_ADD, /*operation*/
                &trans_hdl) < 0) {
        perror("junos_dfw_filter_trans_alloc failed\n");
    }
    
    /*
     * Set up an ordered list of the fields, as required for fast-update filters:
     * 
     * The junos_dfw_filter_prop_ordered_field_list property sets 
     * the list of fields that can be configured in the filter          
     * and establishes the order in which the fields   
     * will be matched: ip proto, followed by source address,     
     * followed by destination address, followed by source port,    
     * followed by destination port.                       
     */
    junos_dfw_filter_field_type_t field_list[] = {
        JUNOS_DFW_FILTER_FIELD_IP_PROTO,
        JUNOS_DFW_FILTER_FIELD_IP_SRC_ADDR,
        JUNOS_DFW_FILTER_FIELD_IP_DEST_ADDR,
        JUNOS_DFW_FILTER_FIELD_SRC_PORT,
        JUNOS_DFW_FILTER_FIELD_DEST_PORT};
    
    if (junos_dfw_filter_prop_ordered_field_list(trans_hdl,
                5, /*num_fields*/
                field_list) < 0) {
        perror("junos_dfw_filter_prop_field_list failed\n");
    }
    
    
    
    /*
     * Start definition of term "session1".
     */
    junos_dfw_term_info_t term_info;
    
    term_info.namestr_key = "session1";
    
    /* 
     * A fast-update filter requires terms of type "prioritised"
     */
    term_info.type = JUNOS_DFW_TERM_TYPE_PRIORITISED;
    term_info.property.priority = 0;
    
    if (junos_dfw_term_start(trans_hdl,
                &term_info;
                JUNOS_DFW_TERM_OP_ADD) < 0) /*operation*/ {
        perror("junos_dfw_term_start failed\n");
    }
    
    /* 
     * Provide match conditions by calling junos_dfw_term_match_XXX functions.
     * 
     */
    u_int32_t src_addr = 0xC0A8010A; /* 192.168.1.10 */
    u_int16_t src_port = 8080;
    u_int32_t dest_addr = 0x1310010A; /* 19.16.1.10 */
    u_int16_t dest_port = 9080;
    
    if (junos_dfw_term_match_src_prefix(trans_hdl,
                &src_addr,
                32, /* prefix_len in bits */
                JUNOS_DFW_FILTER_OP_MATCH) < 0) /*operation*/ {
        perror("junos_dfw_term_match_src_prefix failed\n");
    } else if (junos_dfw_term_match_dest_prefix(trans_hdl,
                &dest_addr,
                32, /* prefix_len in bits */
                JUNOS_DFW_FILTER_OP_MATCH) < 0) /*operation*/ {
        perror("junos_dfw_term_match_dest_prefix failed\n");
    } else if (junos_dfw_term_match_ip_proto(trans_hdl,
                6, /*ip_proto_min = tcp*/
                6, /*ip_proto_max = tcp*/
                JUNOS_DFW_FILTER_OP_MATCH) < 0) /*operation*/ {
        perror("junos_dfw_term_match_ip_proto failed\n");
    } else if (junos_dfw_term_match_src_port(trans_hdl,
                src_port, /* port_min */
                src_port, /* port_max */
                JUNOS_DFW_FILTER_OP_MATCH) < 0) /*operation*/ {
        perror("junos_dfw_term_match_src_port failed\n");
    } else if (junos_dfw_term_match_dest_port(trans_hdl,
                dest_port, /* port_min */
                dest_port, /* port_max */
                JUNOS_DFW_FILTER_OP_MATCH) < 0) /*operation*/ {
        perror("junos_dfw_term_match_dest_port failed\n");
    }
    
    if (junos_dfw_term_action_policer(trans_hdl,
                &policer_info) < 0)  {
        perror("junos_dfw_term_action_policer failed\n");
    }
    
    /* 
     * End definition of term "session1":
     */
    if (junos_dfw_term_end(trans_hdl) < 0) {
        perror("junos_dfw_term_end failed\n");
    }
    
    /* 
     * Configure term "voice-signaling". 
     */
    
    term_info.namestr_key = "voice-signaling";
    src_addr = 0xC0A80100; /* 192.168.1.0 */
    
    term_info.type = DFWD_TERM_TYPE_PRIORITISED;
    term_info.property.priority = 0;
    
    if (junos_dfw_term_start(trans_hdl,
                &term_info;
                DFWD_TERM_OP_ADD) < 0) /*operation*/ {
        perror("junos_dfw_term_start failed\n");
    } else if (junos_dfw_term_match_src_prefix(trans_hdl,
                &src_addr,
                24, /* prefix_len in bits */
                DFWD_FILTER_OP_MATCH) < 0) /*operation*/ {
        perror("junos_dfw_term_match_src_prefix failed\n");
    } else if (junos_dfw_term_match_src_port(trans_hdl,
                7000, /* port_min */
                9000, /* port_max */
                DFWD_FILTER_OP_MATCH) < 0) /*operation*/ {
        perror("junos_dfw_term_match_src_port failed\n");
    } else if (junos_dfw_term_action_policer(trans_hdl,
                &policer_info) < 0)  {
        perror("junos_dfw_term_action_policer failed\n");
    } else if (junos_dfw_term_end(trans_hdl) < 0) {
        perror("junos_dfw_term_end failed\n");
    }
    
    if (junos_dfw_trans_send(session, trans_hdl, id, user_ctx) < 0)  {
        perror("junos_dfw_trans_send failed\n");
    }


At this point, you can apply the filter you have just configured to the input side of an interface (see Sample Code: Applying a Filter to an Interface, next).

Sample Code: Applying a Filter to an Interface

The following code attaches a filter to the input side of interface ge-0/0/0.0. In this example, CLIENT_ID is the identifier that is returned to your application from JUNOS-DFW when you open the session by calling junos_dfw_session_open().

    junos_dfw_trans_handle_t apply_trans_hdl;

    if(junos_dfw_filter_attach_trans_alloc(&filter_info, 
                JUNOS_DFW_FILTER_ATTACH_POINT_INPUT_INTF,
                "ge-0/0/0.0",  &apply_trans_hdl)) {
        perror("junos_dfw_filter_attach_trans_alloc failed\n");
    } else if (junos_dfw_trans_send(session, apply_trans_hdl,
                CLIENT_ID, user_ctx) < 0)  {
        perror("junos_dfw_trans_send failed\n");
    } else 
    /* Transaction handle must be freed as follows if not saved.*/
    if (junos_dfw_trans_handle_free(apply_trans_hdl) < 0)  {
        perror("junos_dfw_trans_handle_free failed\n");
    }

Storing and Retrieving User Data

After initiating the "connect" phase with JUNOS-DFW, the application can associate user-specific information (typically, a pointer to a user session control block) with the session. For example:

    error = junos_dfw_user_data_set(handle_p, &acme_block_p);
    if (error) {
        acme_log("Unable to associate user data\n");
        return -1;
    }
 

Later, in a callback, the application can retrieve the stored information pointer as follows:

    acme_block_s *acme_block_p;
 
    error = junos_dfw_user_data_get(handle_p, &acme_block_p);
    if (error) {
        acme_log("Unable to retrieve user data\n");
        return -1;
    }

2007-2009 Juniper Networks, Inc. All rights reserved. The information contained herein is confidential information of Juniper Networks, Inc., and may not be used, disclosed, distributed, modified, or copied without the prior written consent of Juniper Networks, Inc. in an express license. This information is subject to change by Juniper Networks, Inc. Juniper Networks, the Juniper Networks logo, and JUNOS are registered trademarks of Juniper Networks, Inc. in the United States and other countries. All other trademarks, service marks, registered trademarks, or registered service marks are the property of their respective owners.
Generated on Sun May 30 20:26:47 2010 for Juniper Networks Partner Solution Development Platform JUNOS SDK 10.2R1 by Doxygen 1.4.5