/******************************************************************************* ** ** FILE NAME : ifx_ppa_api_session.c ** PROJECT : PPA ** MODULES : PPA API (Routing/Bridging Acceleration APIs) ** ** DATE : 4 NOV 2008 ** AUTHOR : Xu Liang ** DESCRIPTION : PPA Protocol Stack Hook API Session Operation Functions ** COPYRIGHT : Copyright (c) 2009 ** Lantiq Deutschland GmbH ** Am Campeon 3; 85579 Neubiberg, Germany ** ** For licensing information, see the file 'LICENSE' in the root folder of ** this software module. ** ** HISTORY ** $Date $Author $Comment ** 04 NOV 2008 Xu Liang Initiate Version *******************************************************************************/ /* * #################################### * Head File * #################################### */ /* * Common Head File */ #include //#include //#include //#include //#include //#include //#include //#if defined(CONFIG_IFX_PPA_API_PROC) //#include //#endif //#include //#include //#include //#include //#include /* * PPA Specific Head File */ #include #include #include "ifx_ppa_api_misc.h" #include "ifx_ppa_api_netif.h" #include "ifx_ppa_api_session.h" /* * #################################### * Definition * #################################### */ /* * default settings */ #define DEFAULT_TIMEOUT_IN_SEC 3600 // 1 hour #define DEFAULT_BRIDGING_TIMEOUT_IN_SEC 60 // 1 minute #define DEFAULT_MTU 1500 // IP frame size (including IP header) #define DEFAULT_CH_ID 0 #define DEFAULT_HIT_POLLING_TIME 60 // 1 minute #define DEFAULT_BRIDGING_HIT_POLLING_TIME 2 // 2 seconds /* * hash calculation */ #define SESSION_LIST_HASH_SHIFT 8 #define SESSION_LIST_HASH_BIT_LENGTH 9 #define SESSION_LIST_HASH_MASK ((1 << SESSION_LIST_HASH_BIT_LENGTH) - 1) #define SESSION_LIST_HASH_TABLE_SIZE (1 << (SESSION_LIST_HASH_BIT_LENGTH + 1)) #define SESSION_LIST_HASH_VALUE(x, is_reply) (((((uint32_t)(x) >> SESSION_LIST_HASH_SHIFT) & SESSION_LIST_HASH_MASK) << 1) | ((is_reply) ? 1 : 0)) #define SESSION_LIST_MC_HASH_SHIFT 0 #define SESSION_LIST_MC_HASH_BIT_LENGTH 6 #define SESSION_LIST_MC_HASH_MASK ((1 << SESSION_LIST_MC_HASH_BIT_LENGTH) - 1) #define SESSION_LIST_MC_HASH_TABLE_SIZE (1 << SESSION_LIST_MC_HASH_BIT_LENGTH) #define SESSION_LIST_MC_HASH_VALUE(x) (((uint32_t)(x) >> SESSION_LIST_MC_HASH_SHIFT) & SESSION_LIST_MC_HASH_MASK) #define BRIDGING_SESSION_LIST_HASH_BIT_LENGTH 8 #define BRIDGING_SESSION_LIST_HASH_MASK ((1 << BRIDGING_SESSION_LIST_HASH_BIT_LENGTH) - 1) #define BRIDGING_SESSION_LIST_HASH_TABLE_SIZE (1 << BRIDGING_SESSION_LIST_HASH_BIT_LENGTH) #define BRIDGING_SESSION_LIST_HASH_VALUE(x) ( ( ((uint32_t)((uint8_t *)(x))[4] << 8) | ((uint8_t *)(x))[5] ) & BRIDGING_SESSION_LIST_HASH_MASK ) /* * #################################### * Data Type * #################################### */ /* * #################################### * Declaration * #################################### */ /* * implemented in PPA PPE Low Level Driver (Data Path) */ extern int get_netif_qid_with_pkt(struct sk_buff *skb, void *arg, int is_atm_vcc); // routing session list item operation static INLINE void ppa_init_session_list_item(struct session_list_item *); static INLINE struct session_list_item *ppa_alloc_session_list_item(void); static INLINE void ppa_free_session_list_item(struct session_list_item *); static INLINE void ppa_insert_session_item(struct session_list_item *); static INLINE void ppa_remove_session_item(struct session_list_item *); static void ppa_free_session_list(void); // multicast routing group list item operation static INLINE void ppa_init_mc_group_list_item(struct mc_group_list_item *); static INLINE struct mc_group_list_item *ppa_alloc_mc_group_list_item(void); static INLINE void ppa_free_mc_group_list_item(struct mc_group_list_item *); static INLINE void ppa_insert_mc_group_item(struct mc_group_list_item *); static INLINE void ppa_remove_mc_group_item(struct mc_group_list_item *); static void ppa_free_mc_group_list(void); // routing session timeout help function static INLINE uint32_t ppa_get_default_session_timeout(void); static void ppa_check_hit_stat(unsigned long); // bridging session list item operation static INLINE void ppa_bridging_init_session_list_item(struct bridging_session_list_item *); static INLINE struct bridging_session_list_item *ppa_bridging_alloc_session_list_item(void); static INLINE void ppa_bridging_free_session_list_item(struct bridging_session_list_item *); static INLINE void ppa_bridging_insert_session_item(struct bridging_session_list_item *); static INLINE void ppa_bridging_remove_session_item(struct bridging_session_list_item *); static void ppa_free_bridging_session_list(void); // bridging session timeout help function static INLINE uint32_t ppa_bridging_get_default_session_timeout(void); static void ppa_bridging_check_hit_stat(unsigned long); // help function for special function static INLINE void ppa_remove_routing_sessions_on_netif(PPA_IFNAME *, uint32_t); static INLINE void ppa_remove_mc_groups_on_netif(PPA_IFNAME *); static INLINE void ppa_remove_bridging_sessions_on_netif(PPA_IFNAME *); /* * #################################### * Global Variable * #################################### */ /* * routing session table */ static PPA_LOCK g_session_list_lock; static struct session_list_item *g_session_list_hash_table[SESSION_LIST_HASH_TABLE_SIZE] = {0}; static uint32_t g_session_list_length = 0; static PPA_MEM_CACHE *g_session_item_cache = NULL; static PPA_TIMER g_hit_stat_timer; static uint32_t g_hit_polling_time = DEFAULT_HIT_POLLING_TIME; /* * multicast routing session table */ static PPA_LOCK g_mc_group_list_lock; static struct mc_group_list_item *g_mc_group_list_hash_table[SESSION_LIST_MC_HASH_TABLE_SIZE] = {0}; static uint32_t g_mc_group_list_length = 0; static PPA_MEM_CACHE *g_mc_group_item_cache = NULL; /* * bridging session table */ static PPA_LOCK g_bridging_session_list_lock; static struct bridging_session_list_item *g_bridging_session_list_hash_table[BRIDGING_SESSION_LIST_HASH_TABLE_SIZE] = {0}; static PPA_MEM_CACHE *g_bridging_session_item_cache = NULL; static PPA_TIMER g_bridging_hit_stat_timer; static uint32_t g_bridging_hit_polling_time = DEFAULT_BRIDGING_HIT_POLLING_TIME; /* * #################################### * Extern Variable * #################################### */ /* * #################################### * Local Function * #################################### */ /* * routing session list item operation */ static INLINE void ppa_init_session_list_item(struct session_list_item *p_item) { ppa_memset(p_item, 0, sizeof(*p_item)); p_item->mtu = DEFAULT_MTU; p_item->routing_entry = ~0; p_item->pppoe_entry = ~0; p_item->mtu_entry = ~0; p_item->src_mac_entry = ~0; p_item->out_vlan_entry = ~0; } static INLINE struct session_list_item *ppa_alloc_session_list_item(void) { struct session_list_item *p_item; p_item = ppa_mem_cache_alloc(g_session_item_cache); if ( p_item ) ppa_init_session_list_item(p_item); return p_item; } static INLINE void ppa_free_session_list_item(struct session_list_item *p_item) { ppa_hw_del_session(p_item); ppa_mem_cache_free(p_item, g_session_item_cache); } static INLINE void ppa_insert_session_item(struct session_list_item *p_item) { uint32_t idx; idx = SESSION_LIST_HASH_VALUE(p_item->session, p_item->flags & SESSION_IS_REPLY); ppa_lock_get(&g_session_list_lock); p_item->next = g_session_list_hash_table[idx]; g_session_list_hash_table[idx] = p_item; g_session_list_length++; ppa_lock_release(&g_session_list_lock); } static INLINE void ppa_remove_session_item(struct session_list_item *p_item) { uint32_t idx; struct session_list_item *p_prev, *p_cur; idx = SESSION_LIST_HASH_VALUE(p_item->session, p_item->flags & SESSION_IS_REPLY); p_prev = NULL; ppa_lock_get(&g_session_list_lock); p_cur = g_session_list_hash_table[idx]; while ( p_cur && p_cur != p_item ) { p_prev = p_cur; p_cur = p_cur->next; } if ( p_cur ) { if ( !p_prev ) g_session_list_hash_table[idx] = p_cur->next; else p_prev->next = p_cur->next; p_cur->next = NULL; g_session_list_length--; } ppa_lock_release(&g_session_list_lock); } static void ppa_free_session_list(void) { struct session_list_item *p_item, *p_next; int i; ppa_lock_get(&g_session_list_lock); for ( i = 0; i < NUM_ENTITY(g_session_list_hash_table); i++ ) { for ( p_item = g_session_list_hash_table[i]; p_item; p_item = p_next ) { p_next = p_item->next; ppa_free_session_list_item(p_item); } g_session_list_hash_table[i] = NULL; } ppa_lock_release(&g_session_list_lock); } /* * multicast routing group list item operation */ static INLINE void ppa_init_mc_group_list_item(struct mc_group_list_item *p_item) { ppa_memset(p_item, 0, sizeof(*p_item)); p_item->mc_entry = ~0; p_item->src_mac_entry = ~0; p_item->out_vlan_entry = ~0; } static INLINE struct mc_group_list_item *ppa_alloc_mc_group_list_item(void) { struct mc_group_list_item *p_item; p_item = ppa_mem_cache_alloc(g_mc_group_item_cache); if ( p_item ) ppa_init_mc_group_list_item(p_item); return p_item; } static INLINE void ppa_free_mc_group_list_item(struct mc_group_list_item *p_item) { ppa_hw_del_mc_group(p_item); ppa_mem_cache_free(p_item, g_mc_group_item_cache); } static INLINE void ppa_insert_mc_group_item(struct mc_group_list_item *p_item) { uint32_t idx; idx = SESSION_LIST_MC_HASH_VALUE(p_item->ip_mc_group); ppa_lock_get(&g_mc_group_list_lock); p_item->next = g_mc_group_list_hash_table[idx]; g_mc_group_list_hash_table[idx] = p_item; g_mc_group_list_length++; ppa_lock_release(&g_mc_group_list_lock); } static INLINE void ppa_remove_mc_group_item(struct mc_group_list_item *p_item) { uint32_t idx; struct mc_group_list_item *p_prev, *p_cur; idx = SESSION_LIST_MC_HASH_VALUE(p_item->ip_mc_group); p_prev = NULL; ppa_lock_get(&g_mc_group_list_lock); p_cur = g_mc_group_list_hash_table[idx]; while ( p_cur && p_cur != p_item ) { p_prev = p_cur; p_cur = p_cur->next; } if ( p_cur ) { if ( !p_prev ) g_mc_group_list_hash_table[idx] = p_cur->next; else p_prev->next = p_cur->next; p_cur->next = NULL; g_mc_group_list_length--; } ppa_lock_release(&g_mc_group_list_lock); } static void ppa_free_mc_group_list(void) { struct mc_group_list_item *p_mc_item, *p_mc_next; int i; ppa_lock_get(&g_mc_group_list_lock); for ( i = 0; i < NUM_ENTITY(g_mc_group_list_hash_table); i++ ) { for ( p_mc_item = g_mc_group_list_hash_table[i]; p_mc_item; p_mc_item = p_mc_next ) { p_mc_next = p_mc_item->next; ppa_free_mc_group_list_item(p_mc_item); } g_mc_group_list_hash_table[i] = NULL; } ppa_lock_release(&g_mc_group_list_lock); } /* * routing session timeout help function */ static INLINE uint32_t ppa_get_default_session_timeout(void) { return DEFAULT_TIMEOUT_IN_SEC; } static void ppa_check_hit_stat(unsigned long dummy) { struct session_list_item *p_item; uint32_t i; ppa_lock_get(&g_session_list_lock); for ( i = 0; i < NUM_ENTITY(g_session_list_hash_table); i++ ) for ( p_item = g_session_list_hash_table[i]; p_item; p_item = p_item->next ) if ( test_and_clear_hit_stat(p_item->routing_entry) ) p_item->last_hit_time = ppa_get_time_in_sec(); ppa_lock_release(&g_session_list_lock); ppa_timer_add(&g_hit_stat_timer, g_hit_polling_time); } /* * bridging session list item operation */ static INLINE void ppa_bridging_init_session_list_item(struct bridging_session_list_item *p_item) { ppa_memset(p_item, 0, sizeof(*p_item)); p_item->bridging_entry = ~0; } static INLINE struct bridging_session_list_item *ppa_bridging_alloc_session_list_item(void) { struct bridging_session_list_item *p_item; p_item = ppa_mem_cache_alloc(g_bridging_session_item_cache); if ( p_item ) ppa_bridging_init_session_list_item(p_item); return p_item; } static INLINE void ppa_bridging_free_session_list_item(struct bridging_session_list_item *p_item) { ppa_bridging_hw_del_session(p_item); ppa_mem_cache_free(p_item, g_bridging_session_item_cache); } static INLINE void ppa_bridging_insert_session_item(struct bridging_session_list_item *p_item) { uint32_t idx; idx = BRIDGING_SESSION_LIST_HASH_VALUE(p_item->mac); ppa_lock_get(&g_bridging_session_list_lock); p_item->next = g_bridging_session_list_hash_table[idx]; g_bridging_session_list_hash_table[idx] = p_item; ppa_lock_release(&g_bridging_session_list_lock); } static INLINE void ppa_bridging_remove_session_item(struct bridging_session_list_item *p_item) { uint32_t idx; struct bridging_session_list_item *p_prev, *p_cur; idx = BRIDGING_SESSION_LIST_HASH_VALUE(p_item->mac); p_prev = NULL; ppa_lock_get(&g_bridging_session_list_lock); p_cur = g_bridging_session_list_hash_table[idx]; while ( p_cur && p_cur != p_item ) { p_prev = p_cur; p_cur = p_cur->next; } if ( p_cur ) { if ( !p_prev ) g_bridging_session_list_hash_table[idx] = p_cur->next; else p_prev->next = p_cur->next; p_cur->next = NULL; } ppa_lock_release(&g_bridging_session_list_lock); } static void ppa_free_bridging_session_list(void) { struct bridging_session_list_item *p_brg_item, *p_brg_next; int i; ppa_lock_get(&g_bridging_session_list_lock); for ( i = 0; i < NUM_ENTITY(g_bridging_session_list_hash_table); i++ ) { for ( p_brg_item = g_bridging_session_list_hash_table[i]; p_brg_item; p_brg_item = p_brg_next ) { p_brg_next = p_brg_item->next; ppa_bridging_free_session_list_item(p_brg_item); } g_bridging_session_list_hash_table[i] = NULL; } ppa_lock_release(&g_bridging_session_list_lock); } /* * bridging session timeout help function */ static INLINE uint32_t ppa_bridging_get_default_session_timeout(void) { return DEFAULT_BRIDGING_TIMEOUT_IN_SEC; } static void ppa_bridging_check_hit_stat(unsigned long dummy) { struct bridging_session_list_item *p_item; uint32_t i; ppa_lock_get(&g_bridging_session_list_lock); for ( i = 0; i < NUM_ENTITY(g_bridging_session_list_hash_table); i++ ) for ( p_item = g_bridging_session_list_hash_table[i]; p_item; p_item = p_item->next ) if ( !(p_item->flags & SESSION_STATIC) && test_and_clear_bridging_hit_stat(p_item->bridging_entry) ) p_item->last_hit_time = ppa_get_time_in_sec(); ppa_lock_release(&g_bridging_session_list_lock); ppa_timer_add(&g_bridging_hit_stat_timer, g_bridging_hit_polling_time); } /* * help function for special function */ static INLINE void ppa_remove_routing_sessions_on_netif(PPA_IFNAME *ifname, uint32_t lan_wan_flag) { uint32_t idx; struct session_list_item *p_prev, *p_cur; struct session_list_item *p_list = NULL, *p_to_del; if ( lan_wan_flag == 3 ) { ppa_lock_get(&g_session_list_lock); for ( idx = 0; idx < NUM_ENTITY(g_session_list_hash_table); idx++ ) { p_prev = NULL; p_cur = g_session_list_hash_table[idx]; while ( p_cur ) { if ( ppa_is_netif_name(p_cur->rx_if, ifname) || ppa_is_netif_name(p_cur->tx_if, ifname) ) { p_to_del = p_cur; p_cur = p_cur->next; if ( !p_prev ) g_session_list_hash_table[idx] = p_cur; else p_prev->next = p_cur; g_session_list_length--; p_to_del->next = p_list; p_list = p_to_del; } else { p_prev = p_cur; p_cur = p_cur->next; } } ppa_lock_release(&g_session_list_lock); } } else { uint32_t rx_netif_flag, tx_netif_flag; rx_netif_flag = lan_wan_flag == 1 ? SESSION_LAN_ENTRY : SESSION_WAN_ENTRY; tx_netif_flag = lan_wan_flag == 1 ? SESSION_WAN_ENTRY : SESSION_LAN_ENTRY; ppa_lock_get(&g_session_list_lock); for ( idx = 0; idx < NUM_ENTITY(g_session_list_hash_table); idx++ ) { p_prev = NULL; p_cur = g_session_list_hash_table[idx]; while ( p_cur ) { if ( (ppa_is_netif_name(p_cur->rx_if, ifname) && (p_cur->flags & rx_netif_flag)) || (ppa_is_netif_name(p_cur->tx_if, ifname) && (p_cur->flags & tx_netif_flag)) ) { p_to_del = p_cur; p_cur = p_cur->next; if ( !p_prev ) g_session_list_hash_table[idx] = p_cur; else p_prev->next = p_cur; g_session_list_length--; p_to_del->next = p_list; p_list = p_to_del; } else { p_prev = p_cur; p_cur = p_cur->next; } } ppa_lock_release(&g_session_list_lock); } } for ( p_to_del = p_list; p_to_del; p_to_del = p_list ) { p_list = p_list->next; ppa_free_session_list_item(p_to_del); } } static INLINE void ppa_remove_mc_groups_on_netif(PPA_IFNAME *ifname) { uint32_t idx; struct mc_group_list_item *p_prev, *p_cur; struct mc_group_list_item *p_list = NULL, *p_to_del; int i, j; ppa_lock_get(&g_mc_group_list_lock); for ( idx = 0; idx < NUM_ENTITY(g_mc_group_list_hash_table); idx++ ) { p_prev = NULL; p_cur = g_mc_group_list_hash_table[idx]; while ( p_cur ) { for ( i = 0; i < p_cur->num_ifs; i++ ) if ( ppa_is_netif_name(p_cur->netif[i], ifname) ) p_cur->netif[i] = NULL; for ( i = j = 0; i < p_cur->num_ifs; i++ ) if ( p_cur->netif[i] != NULL ) { if ( i != j ) { p_cur->netif[j] = p_cur->netif[i]; p_cur->ttl[j] = p_cur->ttl[i]; } j++; } if ( j != 0 ) { p_cur->num_ifs = j; p_prev = p_cur; p_cur = p_cur->next; } else { p_to_del = p_cur; p_cur = p_cur->next; if ( !p_prev ) g_mc_group_list_hash_table[idx] = p_cur; else p_prev->next = p_cur; g_mc_group_list_length--; p_to_del->next = p_list; p_list = p_to_del; } } } ppa_lock_release(&g_mc_group_list_lock); for ( p_to_del = p_list; p_to_del; p_to_del = p_list ) { p_list = p_list->next; ppa_free_mc_group_list_item(p_to_del); } } static INLINE void ppa_remove_bridging_sessions_on_netif(PPA_IFNAME *ifname) { uint32_t idx; struct bridging_session_list_item *p_prev, *p_cur; struct bridging_session_list_item *p_list = NULL, *p_to_del; ppa_lock_get(&g_bridging_session_list_lock); for ( idx = 0; idx < NUM_ENTITY(g_bridging_session_list_hash_table); idx++ ) { p_prev = NULL; p_cur = g_bridging_session_list_hash_table[idx]; while ( p_cur ) { if ( ppa_is_netif_name(p_cur->netif, ifname) ) { p_to_del = p_cur; p_cur = p_cur->next; if ( !p_prev ) g_bridging_session_list_hash_table[idx] = p_cur; else p_prev->next = p_cur; p_to_del->next = p_list; p_list = p_to_del; } else { p_prev = p_cur; p_cur = p_cur->next; } } } ppa_lock_release(&g_bridging_session_list_lock); for ( p_to_del = p_list; p_to_del; p_to_del = p_list ) { p_list = p_list->next; ppa_bridging_free_session_list_item(p_to_del); } } /* * #################################### * Global Function * #################################### */ /* * routing session operation */ int32_t ppa_lookup_session(PPA_SESSION *p_session, uint32_t is_reply, struct session_list_item **pp_item) { int32_t ret; struct session_list_item *p_prev, *p_cur; struct session_list_item **pp_header; ASSERT(pp_item != NULL, "pp_item == NULL"); ret = IFX_PPA_SESSION_NOT_ADDED; pp_header = g_session_list_hash_table + SESSION_LIST_HASH_VALUE(p_session, is_reply); p_prev = NULL; ppa_lock_get(&g_session_list_lock); p_cur = *pp_header; while ( p_cur && !ppa_is_session_equal(p_session, p_cur->session) ) { p_prev = p_cur; p_cur = p_cur->next; } if ( p_cur ) { if ( p_prev ) { p_prev->next = p_cur->next; p_cur->next = *pp_header; *pp_header = p_cur; } ret = IFX_PPA_SESSION_EXISTS; } ppa_lock_release(&g_session_list_lock); *pp_item = p_cur; return ret; } int32_t ppa_add_session(PPA_BUF *ppa_buf, PPA_SESSION *p_session, struct session_list_item **pp_item, uint32_t flags) { struct session_list_item *p_item; if ( !p_session ) { p_session = ppa_get_session(ppa_buf); if ( !p_session ) return -1; } p_item = ppa_alloc_session_list_item(); if ( !p_item ) { err("failed in memory allocation"); return IFX_ENOMEM; } dump_list_item(p_item, "ppa_add_session (after init)"); dbg("ppa_get_session(ppa_buf) = %08X", (uint32_t)ppa_get_session(ppa_buf)); p_item->session = p_session; if ( (flags & PPA_F_SESSION_REPLY_DIR) ) p_item->flags |= SESSION_IS_REPLY; p_item->ip_proto = ppa_get_pkt_ip_proto(ppa_buf); p_item->ip_tos = ppa_get_pkt_ip_tos(ppa_buf); p_item->src_ip = ppa_get_pkt_src_ip(ppa_buf); p_item->src_port = ppa_get_pkt_src_port(ppa_buf); p_item->dst_ip = ppa_get_pkt_dst_ip(ppa_buf); p_item->dst_port = ppa_get_pkt_dst_port(ppa_buf); p_item->rx_if = ppa_get_pkt_src_if(ppa_buf); p_item->timeout = ppa_get_default_session_timeout(); p_item->last_hit_time = ppa_get_time_in_sec(); #if defined(SKB_PRIORITY_DEBUG) && SKB_PRIORITY_DEBUG p_item->priority = ppa_get_pkt_priority(ppa_buf); #endif ppa_get_pkt_rx_src_mac_addr(ppa_buf, p_item->src_mac); ppa_insert_session_item(p_item); dump_list_item(p_item, "ppa_add_session (after setting)"); *pp_item = p_item; return 0; } int32_t ppa_update_session(PPA_BUF *ppa_buf, struct session_list_item *p_item, uint32_t flags) { int32_t ret = IFX_SUCCESS; PPA_NETIF *netif; PPA_IPADDR ip; uint32_t port; uint32_t dscp; struct netif_info *rx_ifinfo, *tx_ifinfo; //uint32_t vlan_tag; int f_is_ipoa_or_pppoa = 0; int qid; p_item->tx_if = ppa_get_pkt_dst_if(ppa_buf); /* * update and get rx/tx information */ if ( ppa_netif_update(p_item->rx_if, NULL) != IFX_SUCCESS ) { dbg("failed in collecting info of rx_if (%s)", ppa_get_netif_name(p_item->rx_if)); SET_DBG_FLAG(p_item, SESSION_DBG_RX_IF_UPDATE_FAIL); return IFX_EAGAIN; } if ( ppa_netif_update(p_item->tx_if, NULL) != IFX_SUCCESS ) { dbg("failed in collecting info of tx_if (%s)", ppa_get_netif_name(p_item->tx_if)); SET_DBG_FLAG(p_item, SESSION_DBG_TX_IF_UPDATE_FAIL); return IFX_EAGAIN; } if ( ppa_netif_lookup(ppa_get_netif_name(p_item->rx_if), &rx_ifinfo) != IFX_SUCCESS ) { dbg("failed in getting info structure of rx_if (%s)", ppa_get_netif_name(p_item->rx_if)); SET_DBG_FLAG(p_item, SESSION_DBG_RX_IF_NOT_IN_IF_LIST); return IFX_ENOTPOSSIBLE; } if ( ppa_netif_lookup(ppa_get_netif_name(p_item->tx_if), &tx_ifinfo) != IFX_SUCCESS ) { dbg("failed in getting info structure of tx_if (%s)", ppa_get_netif_name(p_item->tx_if)); SET_DBG_FLAG(p_item, SESSION_DBG_TX_IF_NOT_IN_IF_LIST); ppa_netif_put(rx_ifinfo); return IFX_ENOTPOSSIBLE; } /* * PPPoE is highest level, collect PPPoE information */ p_item->flags &= ~SESSION_VALID_PPPOE; if ( (rx_ifinfo->flags & (NETIF_WAN_IF | NETIF_PPPOE)) == (NETIF_WAN_IF | NETIF_PPPOE) ) { // src interface is WAN and PPPoE p_item->pppoe_session_id = rx_ifinfo->pppoe_session_id; p_item->flags |= SESSION_VALID_PPPOE; SET_DBG_FLAG(p_item, SESSION_DBG_RX_PPPOE); } // if destination interface is PPPoE, it covers the previous setting if ( (tx_ifinfo->flags & (NETIF_WAN_IF | NETIF_PPPOE)) == (NETIF_WAN_IF | NETIF_PPPOE) ) { ASSERT(!(p_item->flags & SESSION_VALID_PPPOE), "both interfaces are WAN PPPoE interface, not possible"); p_item->pppoe_session_id = tx_ifinfo->pppoe_session_id; p_item->flags |= SESSION_VALID_PPPOE; SET_DBG_FLAG(p_item, SESSION_DBG_TX_PPPOE); // adjust MTU to ensure ethernet frame size does not exceed 1518 (without VLAN) p_item->mtu = 1492; } /* * detect bridge and get the real effective device under this bridge * do not support VLAN interface created on bridge */ if ( (rx_ifinfo->flags & (NETIF_BRIDGE | NETIF_PPPOE)) == NETIF_BRIDGE ) // can't handle PPPoE over bridge properly, because src mac info is corrupted { if ( !(rx_ifinfo->flags & NETIF_PHY_IF_GOT) || (netif = ppa_get_netif(rx_ifinfo->phys_netif_name)) == NULL ) { dbg("failed in get underlying interface of PPPoE interface (RX)"); ret = IFX_ENOTPOSSIBLE; goto PPA_UPDATE_SESSION_DONE_SHOTCUT; } while ( (rx_ifinfo->flags & NETIF_BRIDGE) ) { if ( (ret = ppa_get_br_dst_port_with_mac(netif, p_item->src_mac, &netif)) != IFX_SUCCESS ) { SET_DBG_FLAG(p_item, SESSION_DBG_SRC_BRG_IF_NOT_IN_BRG_TBL); if ( ret != IFX_EAGAIN ) ret = IFX_FAILURE; goto PPA_UPDATE_SESSION_DONE_SHOTCUT; } else { CLR_DBG_FLAG(p_item, SESSION_DBG_SRC_BRG_IF_NOT_IN_BRG_TBL); } if ( ppa_netif_update(netif, NULL) != IFX_SUCCESS ) { dbg("failed in collecting info of dst_rx_if (%s)", ppa_get_netif_name(netif)); SET_DBG_FLAG(p_item, SESSION_DBG_RX_IF_UPDATE_FAIL); ret = IFX_EAGAIN; goto PPA_UPDATE_SESSION_DONE_SHOTCUT; } ppa_netif_put(rx_ifinfo); if ( ppa_netif_lookup(ppa_get_netif_name(netif), &rx_ifinfo) != IFX_SUCCESS ) { dbg("failed in getting info structure of dst_rx_if (%s)", ppa_get_netif_name(netif)); SET_DBG_FLAG(p_item, SESSION_DBG_SRC_IF_NOT_IN_IF_LIST); ppa_netif_put(tx_ifinfo); return IFX_ENOTPOSSIBLE; } } } if ( (tx_ifinfo->flags & NETIF_BRIDGE) ) { if ( !(tx_ifinfo->flags & NETIF_PHY_IF_GOT) || (netif = ppa_get_netif(tx_ifinfo->phys_netif_name)) == NULL ) { dbg("failed in get underlying interface of PPPoE interface (TX)"); ret = IFX_ENOTPOSSIBLE; goto PPA_UPDATE_SESSION_DONE_SHOTCUT; } while ( (tx_ifinfo->flags & NETIF_BRIDGE) ) { if ( (ret = ppa_get_br_dst_port(netif, ppa_buf, &netif)) != IFX_SUCCESS ) { SET_DBG_FLAG(p_item, SESSION_DBG_DST_BRG_IF_NOT_IN_BRG_TBL); if ( ret != IFX_EAGAIN ) ret = IFX_FAILURE; goto PPA_UPDATE_SESSION_DONE_SHOTCUT; } else { CLR_DBG_FLAG(p_item, SESSION_DBG_DST_BRG_IF_NOT_IN_BRG_TBL); } if ( ppa_netif_update(netif, NULL) != IFX_SUCCESS ) { dbg("failed in collecting info of dst_tx_if (%s)", ppa_get_netif_name(netif)); SET_DBG_FLAG(p_item, SESSION_DBG_TX_IF_UPDATE_FAIL); ret = IFX_EAGAIN; goto PPA_UPDATE_SESSION_DONE_SHOTCUT; } ppa_netif_put(tx_ifinfo); if ( ppa_netif_lookup(ppa_get_netif_name(netif), &tx_ifinfo) != IFX_SUCCESS ) { dbg("failed in getting info structure of dst_tx_if (%s)", ppa_get_netif_name(netif)); SET_DBG_FLAG(p_item, SESSION_DBG_DST_IF_NOT_IN_IF_LIST); ppa_netif_put(rx_ifinfo); return IFX_ENOTPOSSIBLE; } } } /* * check whether physical port is determined or not */ if ( !(tx_ifinfo->flags & NETIF_PHYS_PORT_GOT) ) { ret = IFX_FAILURE; goto PPA_UPDATE_SESSION_DONE_SHOTCUT; } /* * decide which table to insert session, LAN side table or WAN side table */ if ( (rx_ifinfo->flags & (NETIF_LAN_IF | NETIF_WAN_IF)) == (NETIF_LAN_IF | NETIF_WAN_IF) ) { switch ( tx_ifinfo->flags & (NETIF_LAN_IF | NETIF_WAN_IF) ) { case NETIF_LAN_IF: p_item->flags |= SESSION_WAN_ENTRY; break; case NETIF_WAN_IF: p_item->flags |= SESSION_LAN_ENTRY; break; default: ret = IFX_FAILURE; goto PPA_UPDATE_SESSION_DONE_SHOTCUT; } } else { switch ( rx_ifinfo->flags & (NETIF_LAN_IF | NETIF_WAN_IF) ) { case NETIF_LAN_IF: p_item->flags |= SESSION_LAN_ENTRY; break; case NETIF_WAN_IF: p_item->flags |= SESSION_WAN_ENTRY; break; default: ret = IFX_FAILURE; goto PPA_UPDATE_SESSION_DONE_SHOTCUT; } } /* * collect VLAN information (outer/inner) */ // do not support VLAN interface created on bridge if ( (rx_ifinfo->flags & NETIF_VLAN_CANT_SUPPORT) || (tx_ifinfo->flags & NETIF_VLAN_CANT_SUPPORT) ) { dbg("physical interface has limited VLAN support"); p_item->flags |= SESSION_CAN_NOT_ACCEL; goto PPA_UPDATE_SESSION_DONE_SHOTCUT; } if ( (rx_ifinfo->flags & NETIF_VLAN_OUTER) ) p_item->flags |= SESSION_VALID_OUT_VLAN_RM; if ( (tx_ifinfo->flags & NETIF_VLAN_OUTER) ) { if( tx_ifinfo->out_vlan_netif == NULL ) { p_item->out_vlan_tag = tx_ifinfo->outer_vid; // ignore prio and cfi } else { p_item->out_vlan_tag = ( tx_ifinfo->outer_vid & PPA_VLAN_TAG_MASK ) | ppa_vlan_dev_get_egress_qos_mask(tx_ifinfo->out_vlan_netif, ppa_buf); } p_item->flags |= SESSION_VALID_OUT_VLAN_INS; } if ( (rx_ifinfo->flags & NETIF_VLAN_INNER) ) p_item->flags |= SESSION_VALID_VLAN_RM; if ( (tx_ifinfo->flags & NETIF_VLAN_INNER) ) { if( tx_ifinfo->in_vlan_netif == NULL ) { p_item->new_vci = tx_ifinfo->inner_vid ; // ignore prio and cfi } else { p_item->new_vci = ( tx_ifinfo->inner_vid & PPA_VLAN_TAG_MASK ) | ppa_vlan_dev_get_egress_qos_mask(tx_ifinfo->in_vlan_netif, ppa_buf); } p_item->flags |= SESSION_VALID_VLAN_INS; } /* * decide destination list * if tx interface is based on DSL, determine which PVC it is (QID) */ p_item->dest_ifid = tx_ifinfo->phys_port; if ( (tx_ifinfo->flags & NETIF_PHY_ATM) ) { qid = get_netif_qid_with_pkt(ppa_buf, tx_ifinfo->vcc, 1); if ( qid >= 0 ) p_item->dslwan_qid = qid; else p_item->dslwan_qid = tx_ifinfo->dslwan_qid; p_item->flags |= SESSION_VALID_DSLWAN_QID; if ( (tx_ifinfo->flags & NETIF_EOA) ) { SET_DBG_FLAG(p_item, SESSION_DBG_TX_BR2684_EOA); } else if ( (tx_ifinfo->flags & NETIF_IPOA) ) { p_item->flags |= SESSION_TX_ITF_IPOA; SET_DBG_FLAG(p_item, SESSION_TX_ITF_IPOA); f_is_ipoa_or_pppoa = 1; } else if ( (tx_ifinfo->flags & NETIF_PPPOATM) ) { p_item->flags |= SESSION_TX_ITF_PPPOA; SET_DBG_FLAG(p_item, SESSION_TX_ITF_PPPOA); f_is_ipoa_or_pppoa = 1; } } else { netif = ppa_get_netif(tx_ifinfo->phys_netif_name); if ( g_ppa_dbg_enable & DBG_ENABLE_MASK_PRI_TEST ) //for test qos queue only depends on tos last 4 bits value { ppa_set_pkt_priority( ppa_buf, ppa_get_pkt_ip_tos(ppa_buf) % 8 ); } qid = get_netif_qid_with_pkt(ppa_buf, netif, 0); if ( qid >= 0 ) { p_item->dslwan_qid = qid; p_item->flags |= SESSION_VALID_DSLWAN_QID; } } /* * collect src IP/Port, dest IP/Port information */ // only port change with same IP not supported here, not really useful ip = ppa_get_pkt_src_ip(ppa_buf); if ( ppa_memcmp(&ip, &p_item->src_ip, ppa_get_pkt_ip_len(ppa_buf)) != 0 ) { p_item->nat_ip = ip; p_item->flags |= SESSION_VALID_NAT_IP; port = ppa_get_pkt_src_port(ppa_buf); if ( port != p_item->src_port ) { p_item->nat_port = port; p_item->flags |= SESSION_VALID_NAT_PORT | SESSION_VALID_NAT_SNAT; } } else { ip = ppa_get_pkt_dst_ip(ppa_buf); if ( ppa_memcmp(&ip, &p_item->dst_ip, ppa_get_pkt_ip_len(ppa_buf)) != 0 ) { p_item->nat_ip = ip; p_item->flags |= SESSION_VALID_NAT_IP; port = ppa_get_pkt_dst_port(ppa_buf); if ( port != p_item->dst_port ) { p_item->nat_port = port; p_item->flags |= SESSION_VALID_NAT_PORT; } } } /* * calculate new DSCP value if necessary */ dscp = ppa_get_pkt_ip_tos(ppa_buf); if ( dscp != p_item->ip_tos ) { p_item->new_dscp = dscp >> 2; p_item->flags |= SESSION_VALID_NEW_DSCP; } /* * IPoA/PPPoA does not have MAC address */ if ( f_is_ipoa_or_pppoa ) goto PPA_UPDATE_SESSION_DONE_SHOTCUT; /* * get new dest MAC address for ETH, EoA */ if ( ppa_get_dst_mac(ppa_buf, p_item->session, p_item->dst_mac) != IFX_SUCCESS ) { dbg("session:%x can not get dst mac!", (u32)ppa_get_session(ppa_buf)); SET_DBG_FLAG(p_item, SESSION_DBG_GET_DST_MAC_FAIL); ret = IFX_EAGAIN; } else { dbg("dst_mac = %02x:%02x:%02x:%02x:%02x:%02x\n", (uint32_t)p_item->dst_mac[0], (uint32_t)p_item->dst_mac[1], (uint32_t)p_item->dst_mac[2], (uint32_t)p_item->dst_mac[3], (uint32_t)p_item->dst_mac[4], (uint32_t)p_item->dst_mac[5]); } PPA_UPDATE_SESSION_DONE_SHOTCUT: ppa_netif_put(rx_ifinfo); ppa_netif_put(tx_ifinfo); return ret; } int32_t ppa_update_session_extra(PPA_SESSION_EXTRA *p_extra, struct session_list_item *p_item, uint32_t flags) { if ( (flags & PPA_F_SESSION_NEW_DSCP) ) { if ( p_extra->dscp_remark ) { p_item->flags |= SESSION_VALID_NEW_DSCP; p_item->new_dscp = p_extra->new_dscp; } else p_item->flags &= ~SESSION_VALID_NEW_DSCP; } if ( (flags & PPA_F_SESSION_VLAN) ) { if ( p_extra->vlan_insert ) { p_item->flags |= SESSION_VALID_VLAN_INS; p_item->new_vci = ((uint32_t)p_extra->vlan_prio << 13) | ((uint32_t)p_extra->vlan_cfi << 12) | p_extra->vlan_id; } else { p_item->flags &= ~SESSION_VALID_VLAN_INS; p_item->new_vci = 0; } if ( p_extra->vlan_remove ) p_item->flags |= SESSION_VALID_VLAN_RM; else p_item->flags &= ~SESSION_VALID_VLAN_RM; } if ( (flags & PPA_F_MTU) ) { p_item->flags |= SESSION_VALID_MTU; p_item->mtu = p_extra->mtu; } if ( (flags & PPA_F_SESSION_OUT_VLAN) ) { if ( p_extra->out_vlan_insert ) { p_item->flags |= SESSION_VALID_OUT_VLAN_INS; p_item->out_vlan_tag = p_extra->out_vlan_tag; } else { p_item->flags &= ~SESSION_VALID_OUT_VLAN_INS; p_item->out_vlan_tag = 0; } if ( p_extra->out_vlan_remove ) p_item->flags |= SESSION_VALID_OUT_VLAN_RM; else p_item->flags &= ~SESSION_VALID_OUT_VLAN_RM; } return IFX_SUCCESS; } void ppa_remove_session(struct session_list_item *p_item) { ppa_remove_session_item(p_item); ppa_free_session_list_item(p_item); } void dump_list_item(struct session_list_item *p_item, char *comment) { #if defined(DEBUG_DUMP_LIST_ITEM) && DEBUG_DUMP_LIST_ITEM int8_t strbuf[64]; if ( !(g_ppa_dbg_enable & DBG_ENABLE_MASK_DUMP_ROUTING_SESSION) ) return; if ( comment ) printk("dump_list_item - %s\n", comment); else printk("dump_list_item\n"); printk(" next = %08X\n", (uint32_t)p_item->next); printk(" session = %08X\n", (uint32_t)p_item->session); printk(" ip_proto = %08X\n", p_item->ip_proto); printk(" src_ip = %s\n", ppa_get_pkt_ip_string(p_item->src_ip, p_item->flags & SESSION_IS_IPV6, strbuf)); printk(" src_port = %d\n", p_item->src_port); printk(" src_mac[6] = %s\n", ppa_get_pkt_mac_string(p_item->src_mac, strbuf)); printk(" dst_ip = %s\n", ppa_get_pkt_ip_string(p_item->dst_ip, p_item->flags & SESSION_IS_IPV6, strbuf)); printk(" dst_port = %d\n", p_item->dst_port); printk(" dst_mac[6] = %s\n", ppa_get_pkt_mac_string(p_item->dst_mac, strbuf)); printk(" nat_ip = %s\n", ppa_get_pkt_ip_string(p_item->nat_ip, p_item->flags & SESSION_IS_IPV6, strbuf)); printk(" nat_port = %d\n", p_item->nat_port); printk(" rx_if = %08X\n", (uint32_t)p_item->rx_if); printk(" tx_if = %08X\n", (uint32_t)p_item->tx_if); printk(" timeout = %d\n", p_item->timeout); printk(" last_hit_time = %d\n", p_item->last_hit_time); printk(" num_adds = %d\n", p_item->num_adds); printk(" pppoe_session_id = %d\n", p_item->pppoe_session_id); printk(" new_dscp = %d\n", p_item->new_dscp); printk(" new_vci = %08X\n", p_item->new_vci); printk(" mtu = %d\n", p_item->mtu); printk(" flags = %08X\n", p_item->flags); printk(" routing_entry = %08X\n", p_item->routing_entry); printk(" pppoe_entry = %08X\n", p_item->pppoe_entry); printk(" mtu_entry = %08X\n", p_item->mtu_entry); printk(" src_mac_entry = %08X\n", p_item->src_mac_entry); #endif } int32_t ppa_session_start_iteration(uint32_t *ppos, struct session_list_item **pp_item) { struct session_list_item *p = NULL; int idx; uint32_t l; l = *ppos + 1; ppa_lock_get(&g_session_list_lock); for ( idx = 0; l && idx < NUM_ENTITY(g_session_list_hash_table); idx++ ) { for ( p = g_session_list_hash_table[idx]; p; p = p->next ) if ( !--l ) break; } if ( l == 0 && p ) { ++*ppos; *pp_item = p; return IFX_SUCCESS; } else { *pp_item = NULL; return IFX_FAILURE; } } int32_t ppa_session_iterate_next(uint32_t *ppos, struct session_list_item **pp_item) { uint32_t idx; if ( *pp_item == NULL ) return IFX_FAILURE; if ( (*pp_item)->next != NULL ) { ++*ppos; *pp_item = (*pp_item)->next; return IFX_SUCCESS; } else { for ( idx = SESSION_LIST_HASH_VALUE((*pp_item)->session, (*pp_item)->flags & SESSION_IS_REPLY) + 1; idx < NUM_ENTITY(g_session_list_hash_table); idx++ ) if ( g_session_list_hash_table[idx] != NULL ) { ++*ppos; *pp_item = g_session_list_hash_table[idx]; return IFX_SUCCESS; } *pp_item = NULL; return IFX_FAILURE; } } void ppa_session_stop_iteration(void) { ppa_lock_release(&g_session_list_lock); } /* * routing session hardware/firmware operation */ int32_t ppa_hw_add_session(struct session_list_item *p_item) { uint8_t mac[PPA_ETH_ALEN]; uint32_t routing_entry; uint32_t route_type; uint32_t src_mac_ix = ~0, mtu_ix = ~0, pppoe_ix = ~0; uint32_t out_vlan_ix = ~0; uint32_t new_dscp = 0; uint32_t new_ip = 0; uint32_t new_port = 0; uint32_t dest_list = 0; // Only add session in H/w when the called from the post-NAT hook route_type = (p_item->flags & SESSION_VALID_NAT_IP) ? ((p_item->flags & SESSION_VALID_NAT_PORT) ? IFX_PPA_ROUTE_TYPE_NAPT : IFX_PPA_ROUTE_TYPE_NAT) : IFX_PPA_ROUTE_TYPE_IPV4; if ( (p_item->flags & SESSION_VALID_NAT_IP) ) new_ip = p_item->nat_ip.ip; //since only IPv4 support NAT, translate it to IPv4 format if ( (p_item->flags & SESSION_VALID_NAT_PORT) ) new_port = p_item->nat_port; if ( (p_item->flags & (SESSION_VALID_PPPOE | SESSION_LAN_ENTRY)) == (SESSION_VALID_PPPOE | SESSION_LAN_ENTRY) ) { if ( add_pppoe_entry(p_item->pppoe_session_id, &pppoe_ix) == IFX_SUCCESS ) p_item->pppoe_entry = pppoe_ix; else { dbg("add pppoe_entry error"); SET_DBG_FLAG(p_item, SESSION_DBG_ADD_PPPOE_ENTRY_FAIL); goto SESSION_VALID_PPPOE_ERROR; } } if ( add_mtu_entry(p_item->mtu, &mtu_ix) == IFX_SUCCESS ) { p_item->mtu_entry = mtu_ix; p_item->flags |= SESSION_VALID_MTU; } else { SET_DBG_FLAG(p_item, SESSION_DBG_ADD_MTU_ENTRY_FAIL); goto MTU_ERROR; } if ( !(p_item->flags & SESSION_TX_ITF_IPOA_PPPOA_MASK) ) { ppa_get_netif_hwaddr(p_item->tx_if, mac); if ( add_mac_entry(mac, &src_mac_ix) == IFX_SUCCESS ) { p_item->src_mac_entry = src_mac_ix; p_item->flags |= SESSION_VALID_NEW_SRC_MAC; } else { SET_DBG_FLAG(p_item, SESSION_DBG_ADD_MAC_ENTRY_FAIL); goto NEW_SRC_MAC_ERROR; } } if ( (p_item->flags & SESSION_VALID_OUT_VLAN_INS) ) { if ( add_outer_vlan_entry(p_item->out_vlan_tag, &out_vlan_ix) == IFX_SUCCESS ) p_item->out_vlan_entry = out_vlan_ix; else { dbg("add out_vlan_ix error"); SET_DBG_FLAG(p_item, SESSION_DBG_ADD_OUT_VLAN_ENTRY_FAIL); goto OUT_VLAN_ERROR; } } if ( (p_item->flags & SESSION_VALID_NEW_DSCP) ) new_dscp = p_item->new_dscp; dest_list = 1 << p_item->dest_ifid; dbg("dest_list = %02X", dest_list); if ( add_routing_entry(p_item->flags & SESSION_LAN_ENTRY, p_item->src_ip.ip, p_item->src_port, p_item->dst_ip.ip, p_item->dst_port, p_item->flags & SESSION_IS_TCP, route_type, new_ip, new_port, p_item->dst_mac, src_mac_ix, mtu_ix, p_item->flags & SESSION_VALID_NEW_DSCP, new_dscp, p_item->flags & SESSION_VALID_VLAN_INS, p_item->new_vci, p_item->flags & SESSION_VALID_VLAN_RM, p_item->flags & SESSION_VALID_PPPOE, pppoe_ix, p_item->flags & SESSION_VALID_OUT_VLAN_INS, out_vlan_ix, p_item->flags & SESSION_VALID_OUT_VLAN_RM, p_item->dslwan_qid, dest_list, &routing_entry) == IFX_SUCCESS ) { p_item->routing_entry = routing_entry; p_item->flags |= SESSION_ADDED_IN_HW; return IFX_SUCCESS; } // fail in add_routing_entry dbg("fail in add_routing_entry"); p_item->out_vlan_entry = ~0; del_outer_vlan_entry(out_vlan_ix); OUT_VLAN_ERROR: p_item->src_mac_entry = ~0; del_mac_entry(src_mac_ix); NEW_SRC_MAC_ERROR: p_item->mtu_entry = ~0; del_mtu_entry(mtu_ix); MTU_ERROR: p_item->pppoe_entry = ~0; del_pppoe_entry(pppoe_ix); SESSION_VALID_PPPOE_ERROR: return IFX_EAGAIN; } int32_t ppa_hw_update_session_extra(struct session_list_item *p_item, uint32_t flags) { uint32_t update_flags = 0; uint32_t mtu; uint32_t mtu_ix = ~0; uint32_t out_vlan_tag; uint32_t out_vlan_ix = ~0; if ( (flags & PPA_F_SESSION_NEW_DSCP) ) update_flags |= IFX_PPA_UPDATE_ROUTING_ENTRY_NEW_DSCP_EN | IFX_PPA_UPDATE_ROUTING_ENTRY_NEW_DSCP; if ( (flags & PPA_F_SESSION_VLAN) ) update_flags |= IFX_PPA_UPDATE_ROUTING_ENTRY_VLAN_INS_EN | IFX_PPA_UPDATE_ROUTING_ENTRY_NEW_VCI | IFX_PPA_UPDATE_ROUTING_ENTRY_VLAN_RM_EN; if ( (flags & PPA_F_MTU) ) { if ( get_mtu_entry(p_item->mtu_entry, &mtu) == IFX_SUCCESS ) { if ( mtu == p_item->mtu ) { // entry not changed mtu_ix = p_item->mtu_entry; goto PPA_HW_UPDATE_SESSION_EXTRA_MTU_GOON; } else { // entry changed, so delete old first and create new one later del_mtu_entry(p_item->mtu_entry); p_item->mtu_entry = ~0; } } // create new MTU entry if ( add_mtu_entry(p_item->mtu, &mtu_ix) == IFX_SUCCESS ) { // success p_item->mtu_entry = mtu_ix; update_flags |= IFX_PPA_UPDATE_ROUTING_ENTRY_MTU_IX; } else return IFX_EAGAIN; } PPA_HW_UPDATE_SESSION_EXTRA_MTU_GOON: if ( (flags & PPA_F_SESSION_OUT_VLAN) ) { if ( get_outer_vlan_entry(p_item->out_vlan_entry, &out_vlan_tag) == IFX_SUCCESS ) { if ( out_vlan_tag == p_item->out_vlan_tag ) { // entry not changed out_vlan_ix = p_item->out_vlan_entry; goto PPA_HW_UPDATE_SESSION_EXTRA_OUT_VLAN_GOON; } else { // entry changed, so delete old first and create new one later del_outer_vlan_entry(p_item->out_vlan_entry); p_item->out_vlan_entry = ~0; } } // create new OUT VLAN entry if ( add_outer_vlan_entry(p_item->out_vlan_tag, &out_vlan_ix) == IFX_SUCCESS ) { p_item->out_vlan_entry = out_vlan_ix; update_flags |= IFX_PPA_UPDATE_ROUTING_ENTRY_OUT_VLAN_IX; } else return IFX_EAGAIN; update_flags |= IFX_PPA_UPDATE_ROUTING_ENTRY_OUT_VLAN_INS_EN | IFX_PPA_UPDATE_ROUTING_ENTRY_OUT_VLAN_RM_EN; } PPA_HW_UPDATE_SESSION_EXTRA_OUT_VLAN_GOON: update_routing_entry(p_item->routing_entry, 0, 0, 0, NULL, 0, // src_mac_ix mtu_ix, p_item->flags & SESSION_VALID_NEW_DSCP, p_item->new_dscp, p_item->flags & SESSION_VALID_VLAN_INS, p_item->new_vci, p_item->flags & SESSION_VALID_VLAN_RM, 0, 0, p_item->flags & SESSION_VALID_OUT_VLAN_INS, // f_out_vlan_ins_enable p_item->out_vlan_entry, // out_vlan_ix p_item->flags & SESSION_VALID_OUT_VLAN_RM, // f_out_vlan_rm_enable 0, 0, update_flags); return IFX_SUCCESS; } void ppa_hw_del_session(struct session_list_item *p_item) { if ( (p_item->flags & SESSION_ADDED_IN_HW) ) { // when init, these entry values are ~0, the max the number which can be detected by these functions del_routing_entry(p_item->routing_entry); p_item->routing_entry = ~0; del_outer_vlan_entry(p_item->out_vlan_entry); p_item->out_vlan_entry = ~0; del_pppoe_entry(p_item->pppoe_entry); p_item->pppoe_entry = ~0; del_mtu_entry(p_item->mtu_entry); p_item->mtu_entry = ~0; del_mac_entry(p_item->src_mac_entry); p_item->src_mac_entry = ~0; p_item->flags &= ~SESSION_ADDED_IN_HW; } } /* * multicast routing operation */ int32_t ppa_lookup_mc_group(IPADDR ip_mc_group, struct mc_group_list_item **pp_item) { uint32_t ret; struct mc_group_list_item *p_prev, *p_cur; struct mc_group_list_item **pp_header; ASSERT(pp_item != NULL, "pp_item == NULL"); ret = IFX_PPA_SESSION_NOT_ADDED; pp_header = g_mc_group_list_hash_table + SESSION_LIST_MC_HASH_VALUE(ip_mc_group); p_prev = NULL; ppa_lock_get(&g_mc_group_list_lock); p_cur = *pp_header; while ( p_cur ) { if ( p_cur->ip_mc_group == ip_mc_group ) break; p_prev = p_cur; p_cur = p_cur->next; } if ( p_cur ) { if ( p_prev ) { p_prev->next = p_cur->next; p_cur->next = *pp_header; *pp_header = p_cur; } ret = IFX_PPA_SESSION_EXISTS; } ppa_lock_release(&g_mc_group_list_lock); *pp_item = p_cur; return ret; } int32_t ppa_add_mc_group(PPA_MC_GROUP *p_mc_group, struct mc_group_list_item **pp_item, uint32_t flags) { struct mc_group_list_item *p_item; struct netif_info *p_netif_info; uint32_t bit; uint32_t idx; uint32_t i, bfAccelerate=1, tmp_flag = 0, tmp_out_vlan_tag=0; uint16_t tmp_new_vci=0, bfFirst = 1 ; p_item = ppa_alloc_mc_group_list_item(); if ( !p_item ) return IFX_ENOMEM; p_item->bridging_flag = p_mc_group->bridging_flag; for ( i = 0, bit = 1, idx = 0; i < PPA_MAX_MC_IFS_NUM && idx < p_mc_group->num_ifs; i++, bit <<= 1 ) { if ( p_mc_group->if_mask & bit) { dbg("p_mc_group ifname[%d]=%s\n", i, p_mc_group->array_mem_ifs[i].ifname?p_mc_group->array_mem_ifs[i].ifname: "NULL" ); if ( ppa_netif_lookup(p_mc_group->array_mem_ifs[i].ifname, &p_netif_info) == IFX_SUCCESS ) { // dest interface if ( ppa_netif_update(NULL, p_mc_group->array_mem_ifs[i].ifname) != IFX_SUCCESS || !(p_netif_info->flags & NETIF_PHYS_PORT_GOT) ) { ppa_netif_put(p_netif_info); dbg("Warning: No PHYS found for interface %s", p_mc_group->array_mem_ifs[i].ifname); bfAccelerate = 0; break; } if ( bfFirst ) { /* keep the first interface's flag. Make sure all interface's vlan action should be same, otherwise PPE FW cannot do it */ tmp_flag = p_netif_info->flags; tmp_new_vci = p_netif_info->inner_vid; tmp_out_vlan_tag = p_netif_info->outer_vid; bfFirst = 0; } else { if ( ( tmp_flag & ( NETIF_VLAN_OUTER | NETIF_VLAN_INNER ) ) != ( p_netif_info->flags & ( NETIF_VLAN_OUTER | NETIF_VLAN_INNER ) ) ) { bfAccelerate = 0; dbg("ppa_add_mc_group not same flag (%0x_%0x)\n", tmp_flag & (NETIF_VLAN_OUTER | NETIF_VLAN_INNER ), p_netif_info->flags & (NETIF_VLAN_OUTER | NETIF_VLAN_INNER) ); break; } else if ( tmp_out_vlan_tag != p_netif_info->outer_vid ) { bfAccelerate = 0; dbg("ppa_add_mc_group not same out vlan tag (%0x_%0x)\n", tmp_out_vlan_tag, p_netif_info->outer_vid); break; } else if ( tmp_new_vci != p_netif_info->inner_vid ) { bfAccelerate = 0; dbg("ppa_add_mc_group not same inner vlan (%0x_%0x)\n", tmp_new_vci , p_netif_info->inner_vid); break; } } p_item->dest_ifid |= 1 << p_netif_info->phys_port; // sgh change xuliang's original architecture, but for unicast routing/bridging, still keep old one ASSERT(p_netif_info->flags & NETIF_MAC_ENTRY_CREATED, "ETH physical interface must have MAC address"); p_item->src_mac_entry = p_netif_info->mac_entry; if ( !p_mc_group->bridging_flag ) p_item->flags |= SESSION_VALID_SRC_MAC; // VLAN ASSERT(p_netif_info->flags & NETIF_VLAN_CANT_SUPPORT, "MC processing can support two layers of VLAN only"); if ( (p_netif_info->flags & NETIF_VLAN_OUTER) ) { p_item->out_vlan_tag = p_netif_info->outer_vid; p_item->flags |= SESSION_VALID_OUT_VLAN_INS; } if ( (p_netif_info->flags & NETIF_VLAN_INNER) ) { p_item->new_vci = p_netif_info->inner_vid; p_item->flags |= SESSION_VALID_VLAN_INS; } p_item->netif[idx] = p_netif_info->netif; p_item->ttl[idx] = p_mc_group->array_mem_ifs[i].ttl; p_item->if_mask |= 1 << idx; ppa_netif_put(p_netif_info); idx++; } else { bfAccelerate = 0; dbg("ppa_add_mc_group cannot find the interface:%s)\n", p_mc_group->array_mem_ifs[i].ifname); break; } } } if ( bfAccelerate == 0 || idx == 0 || (!p_mc_group->bridging_flag && !(p_item->flags & SESSION_VALID_SRC_MAC)) ) { ppa_free_mc_group_list_item(p_item); return IFX_FAILURE; } if ( !p_mc_group->bridging_flag ) p_item->flags |= SESSION_VALID_PPPOE; // firmware will remove PPPoE header, if and only if the PPPoE header available // broadcast address p_item->ip_mc_group = p_mc_group->ip_mc_group; if ( p_mc_group->src_ifname && ppa_netif_lookup(p_mc_group->src_ifname, &p_netif_info) == IFX_SUCCESS ) { // src interface if ( ppa_netif_update(NULL, p_mc_group->src_ifname) == IFX_SUCCESS && (p_netif_info->flags & NETIF_PHYS_PORT_GOT) ) { // PPPoE if ( !p_mc_group->bridging_flag && (p_netif_info->flags & NETIF_PPPOE) ) p_item->flags |= SESSION_VALID_PPPOE; // VLAN ASSERT(p_netif_info->flags & NETIF_VLAN_CANT_SUPPORT, "MC processing can support two layers of VLAN only"); if ( (p_netif_info->flags & NETIF_VLAN_OUTER) ) p_item->flags |= SESSION_VALID_OUT_VLAN_RM; if ( (p_netif_info->flags & NETIF_VLAN_INNER) ) p_item->flags |= SESSION_VALID_VLAN_RM; } else /*not allowed to support non-physical interfaces,like bridge */ { ppa_free_mc_group_list_item(p_item); ppa_netif_put(p_netif_info); return IFX_FAILURE; } p_item->src_netif = p_netif_info->netif; ppa_netif_put(p_netif_info); } p_item->num_ifs = idx; p_item->dslwan_qid = p_mc_group->dslwan_qid; if ( (flags & PPA_F_SESSION_VLAN) ) { if ( p_mc_group->vlan_insert ) { p_item->flags |= SESSION_VALID_VLAN_INS; p_item->new_vci = ((uint32_t)p_mc_group->vlan_prio << 13) | ((uint32_t)p_mc_group->vlan_cfi << 12) | (uint32_t)p_mc_group->vlan_id; } else { p_item->flags &= ~SESSION_VALID_VLAN_INS; p_item->new_vci = 0; } if ( p_mc_group->vlan_remove ) p_item->flags |= SESSION_VALID_VLAN_RM; else p_item->flags &= ~SESSION_VALID_VLAN_RM; } if ( (flags & PPA_F_SESSION_OUT_VLAN) ) { if ( p_mc_group->out_vlan_insert ) { p_item->flags |= SESSION_VALID_OUT_VLAN_INS; p_item->out_vlan_tag = p_mc_group->out_vlan_tag; } else { p_item->flags &= ~SESSION_VALID_OUT_VLAN_INS; p_item->out_vlan_tag = 0; } if ( p_mc_group->out_vlan_remove ) p_item->flags |= SESSION_VALID_OUT_VLAN_RM; else p_item->flags &= ~SESSION_VALID_OUT_VLAN_RM; } if ( (flags & PPA_F_SESSION_NEW_DSCP) ) { if ( p_mc_group->new_dscp_en ) { p_item->new_dscp = p_mc_group->new_dscp; p_item->flags |= SESSION_VALID_NEW_DSCP; } else p_item->new_dscp &= ~SESSION_VALID_NEW_DSCP; } ppa_insert_mc_group_item(p_item); *pp_item = p_item; return IFX_SUCCESS; } int32_t ppa_update_mc_group_extra(PPA_SESSION_EXTRA *p_extra, struct mc_group_list_item *p_item, uint32_t flags) { if ( (flags & PPA_F_SESSION_NEW_DSCP) ) { if ( p_extra->dscp_remark ) { p_item->flags |= SESSION_VALID_NEW_DSCP; p_item->new_dscp = p_extra->new_dscp; } else p_item->flags &= ~SESSION_VALID_NEW_DSCP; } if ( (flags & PPA_F_SESSION_VLAN) ) { if ( p_extra->vlan_insert ) { p_item->flags |= SESSION_VALID_VLAN_INS; p_item->new_vci = ((uint32_t)p_extra->vlan_prio << 13) | ((uint32_t)p_extra->vlan_cfi << 12) | p_extra->vlan_id; } else { p_item->flags &= ~SESSION_VALID_VLAN_INS; p_item->new_vci = 0; } if ( p_extra->vlan_remove ) p_item->flags |= SESSION_VALID_VLAN_RM; else p_item->flags &= ~SESSION_VALID_VLAN_RM; } if ( (flags & PPA_F_SESSION_OUT_VLAN) ) { if ( p_extra->out_vlan_insert ) { p_item->flags |= SESSION_VALID_OUT_VLAN_INS; p_item->out_vlan_tag = p_extra->out_vlan_tag; } else { p_item->flags &= ~SESSION_VALID_OUT_VLAN_INS; p_item->out_vlan_tag = 0; } if ( p_extra->out_vlan_remove ) p_item->flags |= SESSION_VALID_OUT_VLAN_RM; else p_item->flags &= ~SESSION_VALID_OUT_VLAN_RM; } if ( p_extra->dslwan_qid_remark ) p_item->dslwan_qid = p_extra->dslwan_qid; return IFX_SUCCESS; } void ppa_remove_mc_group(struct mc_group_list_item *p_item) { ppa_remove_mc_group_item(p_item); ppa_free_mc_group_list_item(p_item); } int32_t ppa_mc_group_start_iteration(uint32_t *ppos, struct mc_group_list_item **pp_item) { struct mc_group_list_item *p = NULL; int idx; uint32_t l; l = *ppos + 1; ppa_lock_get(&g_mc_group_list_lock); for ( idx = 0; l && idx < NUM_ENTITY(g_mc_group_list_hash_table); idx++ ) { for ( p = g_mc_group_list_hash_table[idx]; p; p = p->next ) if ( !--l ) break; } if ( l == 0 && p ) { ++*ppos; *pp_item = p; return IFX_SUCCESS; } else { *pp_item = NULL; return IFX_FAILURE; } } int32_t ppa_mc_group_iterate_next(uint32_t *ppos, struct mc_group_list_item **pp_item) { uint32_t idx; if ( *pp_item == NULL ) return IFX_FAILURE; if ( (*pp_item)->next != NULL ) { ++*ppos; *pp_item = (*pp_item)->next; return IFX_SUCCESS; } else { for ( idx = SESSION_LIST_MC_HASH_VALUE((*pp_item)->ip_mc_group) + 1; idx < NUM_ENTITY(g_mc_group_list_hash_table); idx++ ) if ( g_mc_group_list_hash_table[idx] != NULL ) { ++*ppos; *pp_item = g_mc_group_list_hash_table[idx]; return IFX_SUCCESS; } *pp_item = NULL; return IFX_FAILURE; } } void ppa_mc_group_stop_iteration(void) { ppa_lock_release(&g_mc_group_list_lock); } /* * multicast routing hardware/firmware operation */ int32_t ppa_hw_add_mc_group(struct mc_group_list_item *p_item) { uint32_t entry; uint32_t dest_list = 0; uint32_t out_vlan_ix = ~0; uint32_t route_type; route_type = p_item->bridging_flag ? IFX_PPA_ROUTE_TYPE_NULL : IFX_PPA_ROUTE_TYPE_IPV4; // must be LAN port //dest_list = 1 << p_item->dest_ifid; // sgh remove it since it is already shifted already dest_list = p_item->dest_ifid; // due to multiple destination support, the dest_ifid here is a bitmap of destination rather than ifid if ( (p_item->flags & SESSION_VALID_OUT_VLAN_INS) ) { if ( add_outer_vlan_entry(p_item->out_vlan_tag, &out_vlan_ix) == 0 ) p_item->out_vlan_entry = out_vlan_ix; else { dbg("add out_vlan_ix error"); goto OUT_VLAN_ERROR; } } if ( add_wan_mc_entry(p_item->ip_mc_group, p_item->flags & SESSION_VALID_VLAN_INS, p_item->new_vci, p_item->flags & SESSION_VALID_VLAN_RM, p_item->flags & SESSION_VALID_SRC_MAC, p_item->src_mac_entry, p_item->flags & SESSION_VALID_PPPOE, p_item->flags & SESSION_VALID_OUT_VLAN_INS, p_item->out_vlan_entry, p_item->flags & SESSION_VALID_OUT_VLAN_RM, p_item->flags & SESSION_VALID_NEW_DSCP, // DSCP not supported yet p_item->new_dscp, p_item->dslwan_qid, dest_list, route_type, &entry) == IFX_SUCCESS ) { p_item->mc_entry = entry; p_item->flags |= SESSION_ADDED_IN_HW; return IFX_SUCCESS; } p_item->out_vlan_entry = ~0; del_outer_vlan_entry(out_vlan_ix); OUT_VLAN_ERROR: return IFX_EAGAIN; } int32_t ppa_hw_update_mc_group_extra(struct mc_group_list_item *p_item, uint32_t flags) { uint32_t update_flags = 0; uint32_t out_vlan_tag; uint32_t out_vlan_ix = ~0; if ( (flags & PPA_F_SESSION_NEW_DSCP) ) update_flags |= IFX_PPA_UPDATE_WAN_MC_ENTRY_NEW_DSCP_EN | IFX_PPA_UPDATE_WAN_MC_ENTRY_NEW_DSCP; if ( (flags & PPA_F_SESSION_VLAN) ) update_flags |=IFX_PPA_UPDATE_WAN_MC_ENTRY_VLAN_INS_EN |IFX_PPA_UPDATE_WAN_MC_ENTRY_NEW_VCI | IFX_PPA_UPDATE_WAN_MC_ENTRY_VLAN_RM_EN; if ( (flags & PPA_F_SESSION_OUT_VLAN) ) { if ( get_outer_vlan_entry(p_item->out_vlan_entry, &out_vlan_tag) == IFX_SUCCESS ) { if ( out_vlan_tag == p_item->out_vlan_tag ) { // entry not changed out_vlan_ix = p_item->out_vlan_entry; goto PPA_HW_UPDATE_MC_GROUP_EXTRA_OUT_VLAN_GOON; } else { // entry changed, so delete old first and create new one later del_outer_vlan_entry(p_item->out_vlan_entry); p_item->out_vlan_entry = ~0; } } // create new OUT VLAN entry if ( add_outer_vlan_entry(p_item->out_vlan_tag, &out_vlan_ix) == IFX_SUCCESS ) { p_item->out_vlan_entry = out_vlan_ix; update_flags |= IFX_PPA_UPDATE_WAN_MC_ENTRY_OUT_VLAN_IX; } else { err("add_outer_vlan_entry fail"); return IFX_EAGAIN; } update_flags |= IFX_PPA_UPDATE_WAN_MC_ENTRY_OUT_VLAN_INS_EN | IFX_PPA_UPDATE_WAN_MC_ENTRY_OUT_VLAN_RM_EN ; //IFX_PPA_UPDATE_ROUTING_ENTRY_OUT_VLAN_INS_EN | IFX_PPA_UPDATE_ROUTING_ENTRY_OUT_VLAN_RM_EN; } PPA_HW_UPDATE_MC_GROUP_EXTRA_OUT_VLAN_GOON: update_flags |= IFX_PPA_UPDATE_WAN_MC_ENTRY_DEST_QID; //sgh chnage to update qid, since there is no such flag defined at present update_wan_mc_entry(p_item->mc_entry, p_item->flags & SESSION_VALID_VLAN_INS, p_item->new_vci, p_item->flags & SESSION_VALID_VLAN_RM, p_item->flags & SESSION_VALID_SRC_MAC, p_item->src_mac_entry, p_item->flags & SESSION_VALID_PPPOE, p_item->flags & SESSION_VALID_OUT_VLAN_INS, p_item->out_vlan_entry, p_item->flags & SESSION_VALID_OUT_VLAN_RM, p_item->flags & SESSION_VALID_NEW_DSCP, p_item->new_dscp, p_item->dslwan_qid, 0, update_flags); return IFX_SUCCESS; } void ppa_hw_del_mc_group(struct mc_group_list_item *p_item) { if ( (p_item->flags & SESSION_ADDED_IN_HW) ) { // when init, these entry values are ~0, the max the number which can be detected by these functions del_wan_mc_entry(p_item->mc_entry); p_item->mc_entry = ~0; // taken from netif_info, so don't need to be removed from MAC table p_item->src_mac_entry = ~0; del_outer_vlan_entry(p_item->out_vlan_entry); p_item->out_vlan_entry = ~0; p_item->flags &= ~SESSION_ADDED_IN_HW; } } /* * routing polling timer */ void ppa_set_polling_timer(uint32_t polling_time) { if ( polling_time < g_hit_polling_time ) { // remove timer ppa_timer_del(&g_hit_stat_timer); // timeout can not be zero g_hit_polling_time = polling_time < 1 ? 1 : polling_time; // check hit stat in case the left time is less then the new timeout ppa_check_hit_stat(0); // timer is added in this function } } /* * bridging session operation */ int32_t ppa_bridging_lookup_session(uint8_t *mac, PPA_NETIF *netif, struct bridging_session_list_item **pp_item) { int32_t ret; struct bridging_session_list_item *p_prev, *p_cur; struct bridging_session_list_item **pp_header; ASSERT(pp_item != NULL, "pp_item == NULL"); ret = IFX_PPA_SESSION_NOT_ADDED; pp_header = g_bridging_session_list_hash_table + BRIDGING_SESSION_LIST_HASH_VALUE(mac); p_prev = NULL; ppa_lock_get(&g_bridging_session_list_lock); p_cur = *pp_header; while ( p_cur && ppa_memcmp(mac, p_cur->mac, PPA_ETH_ALEN) != 0 /* !(*(uint32_t *)mac == *(uint32_t *)p_cur->mac && *((uint16_t *)mac + 2) == *((uint16_t *)p_cur->mac + 2)) */ ) { p_prev = p_cur; p_cur = p_cur->next; } if ( p_cur ) { if ( p_prev ) { p_prev->next = p_cur->next; p_cur->next = *pp_header; *pp_header = p_cur; } ret = IFX_PPA_SESSION_EXISTS; } ppa_lock_release(&g_bridging_session_list_lock); *pp_item = p_cur; return ret; } int32_t ppa_bridging_add_session(uint8_t *mac, PPA_NETIF *netif, struct bridging_session_list_item **pp_item, uint32_t flags) { struct bridging_session_list_item *p_item; struct netif_info *ifinfo; if ( ppa_netif_update(netif, NULL) != IFX_SUCCESS ) return IFX_ENOTPOSSIBLE; if ( ppa_netif_lookup(ppa_get_netif_name(netif), &ifinfo) != IFX_SUCCESS ) return IFX_FAILURE; #if !defined(CONFIG_IFX_PPA_API_DIRECTPATH_BRIDGING) if( ifx_ppa_get_ifid_for_netif(netif) > 0 ) return IFX_FAILURE; // no need to learn and program mac address in ppe/switch if directpath bridging feature is disabled #endif p_item = ppa_bridging_alloc_session_list_item(); if ( !p_item ) { ppa_netif_put(ifinfo); return IFX_ENOMEM; } dump_bridging_list_item(p_item, "ppa_bridging_add_session (after init)"); ppa_memcpy(p_item->mac, mac, PPA_ETH_ALEN); p_item->netif = netif; p_item->timeout = ppa_bridging_get_default_session_timeout(); p_item->last_hit_time = ppa_get_time_in_sec(); // TODO: vlan related fields // decide destination list /*yixin: VR9 need to add mac address to switch only if the source mac not from switch port */ if ( !(ifinfo->flags & NETIF_PHYS_PORT_GOT) ) { p_item->dest_ifid = get_number_of_phys_port() + 1; //ppa_netif_put(ifinfo); //return IFX_FAILURE; }else{ p_item->dest_ifid = ifinfo->phys_port; } if ( (ifinfo->flags & NETIF_PHY_ATM) ) p_item->dslwan_qid = ifinfo->dslwan_qid; if ( (flags & PPA_F_STATIC_ENTRY) ) { p_item->flags |= SESSION_STATIC; p_item->timeout = ~0; // max timeout } if ( (flags & PPA_F_DROP_PACKET) ) p_item->flags |= SESSION_DROP; ppa_bridging_insert_session_item(p_item); dump_bridging_list_item(p_item, "ppa_bridging_add_session (after setting)"); *pp_item = p_item; ppa_netif_put(ifinfo); return IFX_SUCCESS; } void ppa_bridging_remove_session(struct bridging_session_list_item *p_item) { ppa_bridging_remove_session_item(p_item); ppa_bridging_free_session_list_item(p_item); } void dump_bridging_list_item(struct bridging_session_list_item *p_item, char *comment) { #if defined(DEBUG_DUMP_LIST_ITEM) && DEBUG_DUMP_LIST_ITEM if ( !(g_ppa_dbg_enable & DBG_ENABLE_MASK_DUMP_BRIDGING_SESSION) ) return; if ( comment ) printk("dump_bridging_list_item - %s\n", comment); else printk("dump_bridging_list_item\n"); printk(" next = %08X\n", (uint32_t)p_item->next); printk(" mac[6] = %02x:%02x:%02x:%02x:%02x:%02x\n", p_item->mac[0], p_item->mac[1], p_item->mac[2], p_item->mac[3], p_item->mac[4], p_item->mac[5]); printk(" netif = %08X\n", (uint32_t)p_item->netif); printk(" timeout = %d\n", p_item->timeout); printk(" last_hit_time = %d\n", p_item->last_hit_time); printk(" flags = %08X\n", p_item->flags); printk(" bridging_entry = %08X\n", p_item->bridging_entry); #endif } int32_t ppa_bridging_session_start_iteration(uint32_t *ppos, struct bridging_session_list_item **pp_item) { struct bridging_session_list_item *p = NULL; int idx; uint32_t l; l = *ppos + 1; ppa_lock_get(&g_bridging_session_list_lock); for ( idx = 0; l && idx < NUM_ENTITY(g_bridging_session_list_hash_table); idx++ ) { for ( p = g_bridging_session_list_hash_table[idx]; p; p = p->next ) if ( !--l ) break; } if ( l == 0 && p ) { ++*ppos; *pp_item = p; return IFX_SUCCESS; } else { *pp_item = NULL; return IFX_FAILURE; } } int32_t ppa_bridging_session_iterate_next(uint32_t *ppos, struct bridging_session_list_item **pp_item) { uint32_t idx; if ( *pp_item == NULL ) return IFX_FAILURE; if ( (*pp_item)->next != NULL ) { ++*ppos; *pp_item = (*pp_item)->next; return IFX_SUCCESS; } else { for ( idx = BRIDGING_SESSION_LIST_HASH_VALUE((*pp_item)->mac) + 1; idx < NUM_ENTITY(g_bridging_session_list_hash_table); idx++ ) if ( g_bridging_session_list_hash_table[idx] != NULL ) { ++*ppos; *pp_item = g_bridging_session_list_hash_table[idx]; return IFX_SUCCESS; } *pp_item = NULL; return IFX_FAILURE; } } void ppa_bridging_session_stop_iteration(void) { ppa_lock_release(&g_bridging_session_list_lock); } /* * bridging session hardware/firmware operation */ int32_t ppa_bridging_hw_add_session(struct bridging_session_list_item *p_item) { uint32_t bridging_entry; uint32_t port; uint32_t dest_list; port = p_item->dest_ifid; if ( (p_item->flags & SESSION_DROP) ) dest_list = 0; // no dest list, dropped else dest_list = 1 << p_item->dest_ifid; if ( add_bridging_entry(port, p_item->mac, p_item->flags & SESSION_SRC_MAC_DROP_EN, p_item->dslwan_qid, dest_list, &bridging_entry) == IFX_SUCCESS ) { p_item->bridging_entry = bridging_entry; p_item->flags |= SESSION_ADDED_IN_HW; return IFX_SUCCESS; } return IFX_FAILURE; } void ppa_bridging_hw_del_session(struct bridging_session_list_item *p_item) { if ( (p_item->flags & SESSION_ADDED_IN_HW) ) { del_bridging_entry(p_item->bridging_entry); p_item->bridging_entry = ~0; p_item->flags &= ~SESSION_ADDED_IN_HW; } } /* * bridging polling timer */ void ppa_bridging_set_polling_timer(uint32_t polling_time) { if ( polling_time < g_bridging_hit_polling_time ) { // remove timer ppa_timer_del(&g_bridging_hit_stat_timer); // timeout can not be zero g_bridging_hit_polling_time = polling_time < 1 ? 1 : polling_time; // check hit stat in case the left time is less then the new timeout ppa_bridging_check_hit_stat(0); // timer is added in this function } } /* * special function */ void ppa_remove_sessions_on_netif(PPA_IFNAME *ifname, int f_is_lan) { struct netif_info *ifinfo; uint32_t lan_wan_flag; // bit 0: lan, bit 1: wan if ( ppa_netif_lookup(ifname, &ifinfo) == IFX_SUCCESS ) lan_wan_flag = 3; else lan_wan_flag = f_is_lan ? 1 : 2; ppa_remove_routing_sessions_on_netif(ifname, lan_wan_flag); if ( lan_wan_flag == 2 ) ppa_remove_mc_groups_on_netif(ifname); if ( lan_wan_flag == 3 ) ppa_remove_bridging_sessions_on_netif(ifname); } /* * #################################### * Init/Cleanup API * #################################### */ int32_t ppa_api_session_manager_init(void) { // start timer ppa_timer_init(&g_hit_stat_timer, ppa_check_hit_stat); ppa_timer_add(&g_hit_stat_timer, g_hit_polling_time); ppa_timer_init(&g_bridging_hit_stat_timer, ppa_bridging_check_hit_stat); ppa_timer_add(&g_bridging_hit_stat_timer, g_bridging_hit_polling_time); return IFX_SUCCESS; } void ppa_api_session_manager_exit(void) { ppa_timer_del(&g_hit_stat_timer); ppa_timer_del(&g_bridging_hit_stat_timer); ppa_free_session_list(); ppa_free_mc_group_list(); ppa_free_bridging_session_list(); ppa_kmem_cache_shrink(g_session_item_cache); ppa_kmem_cache_shrink(g_mc_group_item_cache); ppa_kmem_cache_shrink(g_bridging_session_item_cache); } int32_t ppa_api_session_manager_create(void) { if ( ppa_mem_cache_create("ppa_session_item", sizeof(struct session_list_item), &g_session_item_cache) ) { err("Failed in creating mem cache for routing session list."); goto PPA_API_SESSION_MANAGER_CREATE_FAIL; } if ( ppa_mem_cache_create("mc_group_item", sizeof(struct mc_group_list_item), &g_mc_group_item_cache) ) { err("Failed in creating mem cache for multicast group list."); goto PPA_API_SESSION_MANAGER_CREATE_FAIL; } if ( ppa_mem_cache_create("bridging_sess_item", sizeof(struct bridging_session_list_item), &g_bridging_session_item_cache) ) { err("Failed in creating mem cache for bridging session list."); goto PPA_API_SESSION_MANAGER_CREATE_FAIL; } if ( ppa_lock_init(&g_session_list_lock) ) { err("Failed in creating lock for routing session list."); goto PPA_API_SESSION_MANAGER_CREATE_FAIL; } if ( ppa_lock_init(&g_mc_group_list_lock) ) { err("Failed in creating lock for multicast group list."); goto PPA_API_SESSION_MANAGER_CREATE_FAIL; } if ( ppa_lock_init(&g_bridging_session_list_lock) ) { err("Failed in creating lock for bridging session list."); goto PPA_API_SESSION_MANAGER_CREATE_FAIL; } return IFX_SUCCESS; PPA_API_SESSION_MANAGER_CREATE_FAIL: ppa_api_session_manager_destroy(); return IFX_EIO; } void ppa_api_session_manager_destroy(void) { if ( g_session_item_cache ) { ppa_mem_cache_destroy(g_session_item_cache); g_session_item_cache = NULL; } if ( g_mc_group_item_cache ) { ppa_mem_cache_destroy(g_mc_group_item_cache); g_mc_group_item_cache = NULL; } if ( g_bridging_session_item_cache ) { ppa_mem_cache_destroy(g_bridging_session_item_cache); g_bridging_session_item_cache = NULL; } ppa_lock_destroy(&g_session_list_lock); ppa_lock_destroy(&g_mc_group_list_lock); ppa_lock_destroy(&g_bridging_session_list_lock); } EXPORT_SYMBOL(ppa_bridging_session_start_iteration); EXPORT_SYMBOL(ppa_bridging_session_iterate_next); EXPORT_SYMBOL(ppa_bridging_session_stop_iteration); EXPORT_SYMBOL(ppa_session_start_iteration); EXPORT_SYMBOL(ppa_session_iterate_next); EXPORT_SYMBOL(ppa_session_stop_iteration); EXPORT_SYMBOL(ppa_mc_group_start_iteration); EXPORT_SYMBOL(ppa_mc_group_iterate_next); EXPORT_SYMBOL(ppa_mc_group_stop_iteration);