492 lines
15 KiB
C
492 lines
15 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_diffserv.c,v $ $Revision: 1.24.10.1 $
|
|
* $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_diffserv.c,v $
|
|
* $Author: svc-cmnet $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifndef IPCOM_KERNEL
|
|
#define IPCOM_KERNEL
|
|
#endif
|
|
#include "ipnet_config.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifdef IPNET_USE_DIFFSERV
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_type.h>
|
|
|
|
#include "ipnet_h.h"
|
|
#include "ipnet_classifier.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifdef IPNET_DIFFSERV_CLASSIFIER_MODE_BA
|
|
#define IPNET_DIFFSERV_MAX_MAPPINGS 256
|
|
#endif
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_diffserv_key_hash
|
|
*===========================================================================
|
|
* Description: Computes a hash value for the specified diffserv type.
|
|
* Parameters: name - A diffserv entity name.
|
|
* Returns: The hash value.
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipnet_diffserv_key_hash(const char *name)
|
|
{
|
|
return ipcom_hash_update(name, (int)ipcom_strlen(name), 0);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_diffserv_obj_hash
|
|
*===========================================================================
|
|
* Description: Computes a hash value for the specified diffserv type.
|
|
* Parameters: template - The template for an differv entity.
|
|
* Returns: The hash value.
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipnet_diffserv_obj_hash(Ipnet_diffserv_handlers_template *template)
|
|
{
|
|
return ipcom_hash_update(template->name, (int)ipcom_strlen(template->name), 0);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_diffserv_hash_cmp
|
|
*===========================================================================
|
|
* Description: Compares the type of the template with the specified type.
|
|
* Parameters: template - A template for a diffserv type.
|
|
* type - A diffserv type.
|
|
* Returns: IP_TRUE if the specified type matches the template.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipnet_diffserv_hash_cmp(Ipnet_diffserv_handlers_template *template, const char *name)
|
|
{
|
|
return ipcom_strcmp(template->name, name) == 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_diffserv_register_ctor
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_diffserv_register_ctor(Ipnet_diffserv_handlers_template *t)
|
|
{
|
|
if (ipcom_hash_add(ipnet->diffserv.meter_marker_ctor_db, t) != IPCOM_SUCCESS)
|
|
return -IP_ERRNO_ENOMEM;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_diffserv_input
|
|
*===========================================================================
|
|
* Description: Inputs a packet that will be processed according to the
|
|
* rules for its class.
|
|
* Parameters: dst - destination cache entry.
|
|
* pkt - A IP datagram.
|
|
* dst_cache_rx_ctor - constructor for destination cache entry
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ipnet_dst_cache *
|
|
ipnet_diffserv_input(Ipnet_dst_cache *dst,
|
|
Ipcom_pkt *pkt,
|
|
Ipnet_dst_cache_domain_ctor dst_cache_rx_ctor)
|
|
{
|
|
Ipnet_diffserv_handlers *h;
|
|
Ip_u8 class;
|
|
|
|
#ifdef IPNET_DIFFSERV_CLASSIFIER_MODE_BA
|
|
class = ipnet_diffserv_get_ds_field(pkt);
|
|
if (IP_BIT_ISFALSE(ipnet->diffserv.mapping_active[class >> 5], 1u << (class & 0x1f)))
|
|
return dst;
|
|
h = ipnet->diffserv.handlers[ipnet->diffserv.ds_to_handler_map[class]];
|
|
#else
|
|
int tag = ipnet_classifier_pkt_to_tag(ipnet->diffserv.classifier, pkt);
|
|
if (tag < 0)
|
|
/*
|
|
* No meter/maker mapping installed for this flow
|
|
*/
|
|
return dst;
|
|
ip_assert(tag < IPNET_DIFFSERV_MAX_HANDLERS);
|
|
h = ipnet->diffserv.handlers[tag];
|
|
#endif
|
|
|
|
ip_assert(h != IP_NULL);
|
|
|
|
if (h->meter_input)
|
|
h->meter_input(h, pkt);
|
|
|
|
h->marker_input(h, pkt);
|
|
|
|
/*
|
|
* Check if the DS field has changed
|
|
*/
|
|
class = ipnet_diffserv_get_ds_field(pkt);
|
|
if (dst->flow_spec.ds != class)
|
|
{
|
|
Ipnet_flow_spec flow_spec = dst->flow_spec;
|
|
|
|
flow_spec.ds = class;
|
|
|
|
dst = ipnet_dst_cache_get(&flow_spec);
|
|
if (IP_UNLIKELY(dst == IP_NULL))
|
|
{
|
|
int ret = ipnet_dst_cache_new(&flow_spec,
|
|
dst_cache_rx_ctor,
|
|
&dst);
|
|
if (ret < 0)
|
|
dst = ipnet_dst_cache_blackhole();
|
|
}
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_diffserv_get_ds_field
|
|
*===========================================================================
|
|
* Description: Returns the value of the type-of-service field (IPv4) or
|
|
* traffic class (IPv6).
|
|
* Parameters: pkt - An IP datagram.
|
|
* Returns: The Differentiated Services Field.
|
|
e *
|
|
*/
|
|
IP_GLOBAL Ip_u8
|
|
ipnet_diffserv_get_ds_field(Ipcom_pkt *pkt)
|
|
{
|
|
switch (pkt->data[pkt->start] & 0xf0)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case 0x40:
|
|
return ((Ipnet_pkt_ip *) ipcom_pkt_get_data(pkt, 0))->tos;
|
|
#endif /* IPCOM_USE_INET */
|
|
#ifdef IPCOM_USE_INET6
|
|
case 0x60:
|
|
return IPNET_IP6_GET_CLASS((Ipnet_pkt_ip6 *) ipcom_pkt_get_data(pkt, 0));
|
|
#endif /* IPCOM_USE_INET6 */
|
|
default:
|
|
IP_PANIC2();
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_diffserv_set_ds_field
|
|
*===========================================================================
|
|
* Description: Set the TOS field (IPv4) or traffic class (IPv6) to the
|
|
* specified value.
|
|
* Parameters: pkt - An IP datagram.
|
|
* ds - The Differentiated Services Field.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipnet_diffserv_set_ds_field(Ipcom_pkt *pkt, Ip_u8 ds)
|
|
{
|
|
switch (pkt->data[pkt->start] & 0xf0)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case 0x40:
|
|
{
|
|
Ipnet_pkt_ip *iphdr = ipcom_pkt_get_iphdr(pkt);
|
|
iphdr->tos = ds;
|
|
/*
|
|
* Incremental checksum is done on all IP datagrams that is
|
|
* forwarded, so the full checksum must be recalcuated here.
|
|
*/
|
|
iphdr->sum = 0;
|
|
iphdr->sum = ipcom_in_checksum(iphdr,
|
|
(Ip_size_t)IPNET_IP4_GET_HDR_OCTET_LEN(iphdr));
|
|
break;
|
|
}
|
|
#endif /* IPCOM_USE_INET */
|
|
#ifdef IPCOM_USE_INET6
|
|
case 0x60:
|
|
IPNET_IP6_SET_CLASS((Ipnet_pkt_ip6 *) ipcom_pkt_get_iphdr(pkt), ds);
|
|
break;
|
|
#endif /* IPCOM_USE_INET6 */
|
|
default:
|
|
IP_PANIC();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_diffserv_ioctl
|
|
*===========================================================================
|
|
* Description: DiffServ public API handler.
|
|
* Parameters: request - The operation to perform
|
|
* argp - The argument to the operation.
|
|
* Returns: 0 = success
|
|
* <0 = error code.
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipnet_diffserv_ioctl(Ip_u32 request, void *argp)
|
|
{
|
|
Ipnet_diffserv_handlers_template *t;
|
|
Ipnet_diffserv_handlers *h;
|
|
int i;
|
|
struct Ipnet_ds *ds = argp;
|
|
struct Ipnet_ds_map *map = argp;
|
|
#ifndef IPNET_DIFFSERV_CLASSIFIER_MODE_BA
|
|
struct Ipnet_ds_filter *filter = argp;
|
|
#endif
|
|
|
|
switch (request)
|
|
{
|
|
#ifdef IPNET_DIFFSERV_CLASSIFIER_MODE_BA
|
|
case IPNET_SIOCXADSMAP:
|
|
if (map->filter_id < 0 || map->filter_id >= IPNET_DIFFSERV_MAX_MAPPINGS
|
|
|| map->ds_id < 0 || map->ds_id >= IPNET_DIFFSERV_MAX_HANDLERS)
|
|
return -IP_ERRNO_EINVAL;
|
|
|
|
if (ipnet->diffserv.handlers[map->ds_id] == IP_NULL)
|
|
{
|
|
IPCOM_LOG1(WARNING, "DiffServ: Tried to add a mapping for a non-existing DS ID: %d",
|
|
map->ds_id);
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
ipnet->diffserv.ds_to_handler_map[map->filter_id] =(Ip_u8) map->ds_id;
|
|
IP_BIT_SET(ipnet->diffserv.mapping_active[map->filter_id >> 5],
|
|
1u << (map->filter_id & 0x1f));
|
|
break;
|
|
|
|
case IPNET_SIOCXDDSMAP:
|
|
if (map->filter_id < 0 || map->filter_id > IPNET_DIFFSERV_MAX_MAPPINGS)
|
|
return -IP_ERRNO_EINVAL;
|
|
IP_BIT_CLR(ipnet->diffserv.mapping_active[map->filter_id >> 5],
|
|
1 << (map->filter_id & 0x1f));
|
|
break;
|
|
|
|
#else
|
|
case IPNET_SIOCXADSMAP:
|
|
if (map->ds_id < 0 || map->ds_id > IPNET_DIFFSERV_MAX_HANDLERS)
|
|
return -IP_ERRNO_EINVAL;
|
|
|
|
if (ipnet->diffserv.handlers[map->ds_id] == IP_NULL)
|
|
{
|
|
IPCOM_LOG1(WARNING, "DiffServ: Tried to add a mapping for a non-existing DS ID: %d",
|
|
map->ds_id);
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
return ipnet_classifier_set_tag(ipnet->diffserv.classifier, map->filter_id, map->ds_id);
|
|
|
|
case IPNET_SIOCXDDSMAP:
|
|
ipnet_classifier_invalidate_tag(ipnet->diffserv.classifier, map->ds_id);
|
|
return 0;
|
|
|
|
case IPNET_SIOCXADSFILTER:
|
|
filter->id = ipnet_classifier_get_smallest_unused_id(ipnet->diffserv.classifier);
|
|
return ipnet_classifier_add_mapping(ipnet->diffserv.classifier, &filter->rule, filter->id);
|
|
|
|
case IPNET_SIOCXDDSFILTER:
|
|
return ipnet_classifier_del_mapping(ipnet->diffserv.classifier, filter->id);
|
|
|
|
case IPNET_SIOCXGDSFILTER:
|
|
return ipnet_classifier_get_filter(ipnet->diffserv.classifier,
|
|
(struct Ipnet_ds_get_infor*)argp);
|
|
case IPNET_SIOCXGDSNEXTFILTER:
|
|
return ipnet_classifier_get_next_filter(ipnet->diffserv.classifier,
|
|
(struct Ipnet_ds_get_infor*)argp);
|
|
|
|
#endif /* IPNET_DIFFSERV_CLASSIFIER_MODE_BA */
|
|
|
|
case IPNET_SIOCXDSCREATE:
|
|
for (i = 0; i < IPNET_DIFFSERV_MAX_HANDLERS; i++)
|
|
if (ipnet->diffserv.handlers[i] == IP_NULL)
|
|
{
|
|
t = ipcom_hash_get(ipnet->diffserv.meter_marker_ctor_db, ds->name);
|
|
if (t == IP_NULL)
|
|
return -IP_ERRNO_EINVAL;
|
|
ds->id = i;
|
|
return t->create(&ds->d, &ipnet->diffserv.handlers[i]);
|
|
}
|
|
|
|
return -IP_ERRNO_ENOBUFS;
|
|
|
|
case IPNET_SIOCXDSDESTROY:
|
|
if (ds->id < 0 || ds->id >= IPNET_DIFFSERV_MAX_HANDLERS)
|
|
return -IP_ERRNO_EINVAL;
|
|
|
|
h = ipnet->diffserv.handlers[ds->id];
|
|
if (h == IP_NULL)
|
|
return -IP_ERRNO_ESRCH;
|
|
|
|
ipnet->diffserv.handlers[ds->id] = IP_NULL;
|
|
h->destroy(h);
|
|
|
|
#ifdef IPNET_DIFFSERV_CLASSIFIER_MODE_BA
|
|
/* Deactivate all mappings to this handler */
|
|
for (i = 0; i < IPNET_DIFFSERV_MAX_MAPPINGS; i++)
|
|
if (ipnet->diffserv.ds_to_handler_map[i] == ds->id)
|
|
IP_BIT_CLR(ipnet->diffserv.mapping_active[map->filter_id >> 5],
|
|
1 << (map->filter_id & 0x1f));
|
|
#else
|
|
ipnet_classifier_invalidate_tag(ipnet->diffserv.classifier, ds->id);
|
|
#endif
|
|
|
|
break;
|
|
|
|
default:
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_diffserv_init
|
|
*===========================================================================
|
|
* Description: Initializes the DiffServ module.
|
|
* Parameters:
|
|
* Returns: 0 = success.
|
|
* <0 = error code.
|
|
*/
|
|
IP_GLOBAL int
|
|
ipnet_diffserv_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ipnet->diffserv.meter_marker_ctor_db
|
|
= ipcom_hash_new((Ipcom_hash_obj_func) ipnet_diffserv_obj_hash,
|
|
(Ipcom_hash_key_func) ipnet_diffserv_key_hash,
|
|
(Ipcom_hash_cmp_func) ipnet_diffserv_hash_cmp);
|
|
if (ipnet->diffserv.meter_marker_ctor_db == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(CRIT, "DiffServ: Out of memory when creating meter/marker database");
|
|
return -IP_ERRNO_ENOMEM;
|
|
}
|
|
|
|
/* All known handlers constructors must be registered here */
|
|
if (0 > (ret = ipnet_diffserv_register_ctor(ipnet_diffserv_srtcm_template())))
|
|
return ret;
|
|
if (0 > (ret = ipnet_diffserv_register_ctor(ipnet_diffserv_sm_template())))
|
|
return ret;
|
|
|
|
#ifndef IPNET_DIFFSERV_CLASSIFIER_MODE_BA
|
|
ipnet->diffserv.classifier = ipnet_classifier_new();
|
|
if (IP_NULL == ipnet->diffserv.classifier)
|
|
{
|
|
IPCOM_LOG0(CRIT, "DiffServ: Out of memory when creating classifier");
|
|
return -IP_ERRNO_ENOMEM;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
#else
|
|
int ipnet_diffserv_empty_file;
|
|
#endif /* IPNET_USE_DIFFSERV */
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|