• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
3 
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are
6 met:
7 * Redistributions of source code must retain the above copyright
8   notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above
10   copyright notice, this list of conditions and the following
11   disclaimer in the documentation and/or other materials provided
12   with the distribution.
13 * Neither the name of The Linux Foundation nor the names of its
14   contributors may be used to endorse or promote products derived
15   from this software without specific prior written permission.
16 
17 THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.Z
28 */
29 /*!
30   @file
31   IPACM_OffloadManager.cpp
32 
33   @brief
34   This file implements the basis Iface functionality.
35 
36   @Author
37   Skylar Chang
38 
39 */
40 #include <IPACM_OffloadManager.h>
41 #include <sys/ioctl.h>
42 #include <net/if.h>
43 #include <string.h>
44 #include "IPACM_ConntrackClient.h"
45 #include "IPACM_ConntrackListener.h"
46 #include "IPACM_Iface.h"
47 #include "IPACM_Config.h"
48 #include <unistd.h>
49 
50 const char *IPACM_OffloadManager::DEVICE_NAME = "/dev/wwan_ioctl";
51 
52 /* NatApp class Implementation */
53 IPACM_OffloadManager *IPACM_OffloadManager::pInstance = NULL;
54 int IPACM_OffloadManager::num_offload_v4_tethered_iface = 0;
55 
IPACM_OffloadManager()56 IPACM_OffloadManager::IPACM_OffloadManager()
57 {
58 	default_gw_index = INVALID_IFACE;
59 	upstream_v4_up = false;
60 	upstream_v6_up = false;
61 	memset(event_cache, 0, MAX_EVENT_CACHE*sizeof(framework_event_cache));
62 	latest_cache_index = 0;
63 	elrInstance = NULL;
64 	touInstance = NULL;
65 	return ;
66 }
67 
registerEventListener(IpaEventListener * eventlistener)68 RET IPACM_OffloadManager::registerEventListener(IpaEventListener* eventlistener)
69 {
70 	RET result = SUCCESS;
71 	if (elrInstance == NULL) {
72 		IPACMDBG_H("get registerEventListener \n");
73 		elrInstance = eventlistener;
74 	} else {
75 		IPACMDBG_H("already have EventListener previously, override \n");
76 		elrInstance = eventlistener;
77 		result = FAIL_INPUT_CHECK;
78 	}
79 	return SUCCESS;
80 }
81 
unregisterEventListener(IpaEventListener *)82 RET IPACM_OffloadManager::unregisterEventListener(IpaEventListener* )
83 {
84 	RET result = SUCCESS;
85 	if (elrInstance)
86 		elrInstance = NULL;
87 	else {
88 		IPACMDBG_H("already unregisterEventListener previously \n");
89 		result = SUCCESS_DUPLICATE_CONFIG;
90 	}
91 	return SUCCESS;
92 }
93 
registerCtTimeoutUpdater(ConntrackTimeoutUpdater * timeoutupdater)94 RET IPACM_OffloadManager::registerCtTimeoutUpdater(ConntrackTimeoutUpdater* timeoutupdater)
95 {
96 	RET result = SUCCESS;
97 	if (touInstance == NULL)
98 	{
99 		IPACMDBG_H("get ConntrackTimeoutUpdater \n");
100 		touInstance = timeoutupdater;
101 	} else {
102 		IPACMDBG_H("already have ConntrackTimeoutUpdater previously, override \n");
103 		touInstance = timeoutupdater;
104 		result = FAIL_INPUT_CHECK;
105 	}
106 	return SUCCESS;
107 }
108 
unregisterCtTimeoutUpdater(ConntrackTimeoutUpdater *)109 RET IPACM_OffloadManager::unregisterCtTimeoutUpdater(ConntrackTimeoutUpdater* )
110 {
111 	RET result = SUCCESS;
112 	if (touInstance)
113 		touInstance = NULL;
114 	else {
115 		IPACMDBG_H("already unregisterCtTimeoutUpdater previously \n");
116 		result = SUCCESS_DUPLICATE_CONFIG;
117 	}
118 	return SUCCESS;
119 }
120 
provideFd(int fd,unsigned int groups)121 RET IPACM_OffloadManager::provideFd(int fd, unsigned int groups)
122 {
123 	IPACM_ConntrackClient *cc;
124 	int on = 1, rel;
125 	struct sockaddr_nl	local;
126 	unsigned int addr_len;
127 
128 	cc = IPACM_ConntrackClient::GetInstance();
129 
130 	if(!cc)
131 	{
132 		IPACMERR("Init failed: cc %p\n", cc);
133 		return FAIL_HARDWARE;
134 	}
135 
136 	/* check socket name */
137 	memset(&local, 0, sizeof(struct sockaddr_nl));
138 	addr_len = sizeof(local);
139 	getsockname(fd, (struct sockaddr *)&local, (socklen_t *)&addr_len);
140 	IPACMDBG_H(" FD %d, nl_pad %d nl_pid %u\n", fd, local.nl_pad, local.nl_pid);
141 
142 	/* add the check if getting FDs already or not */
143 	if(cc->fd_tcp > -1 && cc->fd_udp > -1) {
144 		IPACMDBG_H("has valid FDs fd_tcp %d, fd_udp %d, ignore fd %d.\n", cc->fd_tcp, cc->fd_udp, fd);
145 		return SUCCESS;
146 	}
147 
148 	if (groups == cc->subscrips_tcp) {
149 		cc->fd_tcp = dup(fd);
150 		IPACMDBG_H("Received fd %d with groups %d.\n", fd, groups);
151 		/* set netlink buf */
152 		rel = setsockopt(cc->fd_tcp, SOL_NETLINK, NETLINK_NO_ENOBUFS, &on, sizeof(int) );
153 		if (rel == -1)
154 		{
155 			IPACMERR( "setsockopt returned error code %d ( %s )", errno, strerror( errno ) );
156 		}
157 	} else if (groups == cc->subscrips_udp) {
158 		cc->fd_udp = dup(fd);
159 		IPACMDBG_H("Received fd %d with groups %d.\n", fd, groups);
160 		/* set netlink buf */
161 		rel = setsockopt(cc->fd_tcp, SOL_NETLINK, NETLINK_NO_ENOBUFS, &on, sizeof(int) );
162 		if (rel == -1)
163 		{
164 			IPACMERR( "setsockopt returned error code %d ( %s )", errno, strerror( errno ) );
165 		}
166 	} else {
167 		IPACMERR("Received unexpected fd with groups %d.\n", groups);
168 	}
169 	if(cc->fd_tcp >0 && cc->fd_udp >0) {
170 		IPACMDBG_H(" Got both fds from framework, start conntrack listener thread.\n");
171 		CtList->CreateConnTrackThreads();
172 	}
173 	return SUCCESS;
174 }
175 
clearAllFds()176 RET IPACM_OffloadManager::clearAllFds()
177 {
178 
179 	/* IPACM needs to kee old FDs, can't clear */
180 	IPACMDBG_H("Still use old Fds, can't clear \n");
181 	return SUCCESS;
182 }
183 
isStaApSupported()184 bool IPACM_OffloadManager::isStaApSupported()
185 {
186 	return true;
187 }
188 
189 
setLocalPrefixes(std::vector<Prefix> &)190 RET IPACM_OffloadManager::setLocalPrefixes(std::vector<Prefix> &/* prefixes */)
191 {
192 	return SUCCESS;
193 }
194 
addDownstream(const char * downstream_name,const Prefix & prefix)195 RET IPACM_OffloadManager::addDownstream(const char * downstream_name, const Prefix &prefix)
196 {
197 	int index;
198 	ipacm_cmd_q_data evt;
199 	ipacm_event_ipahal_stream *evt_data;
200 	bool cache_need = false;
201 
202 	IPACMDBG_H("addDownstream name(%s), ip-family(%d) \n", downstream_name, prefix.fam);
203 
204 	if (prefix.fam == V4) {
205 		IPACMDBG_H("subnet info v4Addr (%x) v4Mask (%x)\n", prefix.v4Addr, prefix.v4Mask);
206 	} else {
207 		IPACMDBG_H("subnet info v6Addr: %08x:%08x:%08x:%08x \n",
208 							prefix.v6Addr[0], prefix.v6Addr[1], prefix.v6Addr[2], prefix.v6Addr[3]);
209 		IPACMDBG_H("subnet info v6Mask: %08x:%08x:%08x:%08x \n",
210 							prefix.v6Mask[0], prefix.v6Mask[1], prefix.v6Mask[2], prefix.v6Mask[3]);
211 	}
212 
213 	/* check if netdev valid on device */
214 	if(ipa_get_if_index(downstream_name, &index))
215 	{
216 		IPACMERR("fail to get iface index.\n");
217 		return FAIL_INPUT_CHECK;
218 	}
219 
220 	/* Iface is valid, add to list if not present */
221 	if (std::find(valid_ifaces.begin(), valid_ifaces.end(), std::string(downstream_name)) == valid_ifaces.end())
222 	{
223 		/* Iface is new, add it to the list */
224 		valid_ifaces.push_back(downstream_name);
225 		IPACMDBG_H("add iface(%s) to list\n", downstream_name);
226 	}
227 
228 	/* check if upstream netdev driver finished its configuration on IPA-HW for ipv4 and ipv6 */
229 	if (prefix.fam == V4 && IPACM_Iface::ipacmcfg->CheckNatIfaces(downstream_name, IPA_IP_v4))
230 		cache_need = true;
231 	if (prefix.fam == V6 && IPACM_Iface::ipacmcfg->CheckNatIfaces(downstream_name, IPA_IP_v6))
232 		cache_need = true;
233 
234 	if (cache_need)
235 	{
236 		IPACMDBG_H("addDownstream name(%s) currently not support in ipa \n", downstream_name);
237 
238 		/* copy to the cache */
239 		for(int i = 0; i < MAX_EVENT_CACHE ;i++)
240 		{
241 			if (latest_cache_index >= 0)
242 			{
243 				if(event_cache[latest_cache_index].valid == false)
244 				{
245 					//do the copy
246 					event_cache[latest_cache_index].valid = true;
247 					event_cache[latest_cache_index].event = IPA_DOWNSTREAM_ADD;
248 					memcpy(event_cache[latest_cache_index].dev_name, downstream_name,
249 						sizeof(event_cache[latest_cache_index].dev_name));
250 					memcpy(&event_cache[latest_cache_index].prefix_cache, &prefix,
251 						sizeof(event_cache[latest_cache_index].prefix_cache));
252 					if (prefix.fam == V4) {
253 						IPACMDBG_H("cache event(%d) subnet info v4Addr (%x) v4Mask (%x) dev(%s) on entry (%d)\n",
254 							event_cache[latest_cache_index].event,
255 							event_cache[latest_cache_index].prefix_cache.v4Addr,
256 							event_cache[latest_cache_index].prefix_cache.v4Mask,
257 							event_cache[latest_cache_index].dev_name,
258 							latest_cache_index);
259 					} else {
260 						IPACMDBG_H("cache event (%d) v6Addr: %08x:%08x:%08x:%08x \n",
261 							event_cache[latest_cache_index].event,
262 							event_cache[latest_cache_index].prefix_cache.v6Addr[0],
263 							event_cache[latest_cache_index].prefix_cache.v6Addr[1],
264 							event_cache[latest_cache_index].prefix_cache.v6Addr[2],
265 							event_cache[latest_cache_index].prefix_cache.v6Addr[3]);
266 						IPACMDBG_H("subnet v6Mask: %08x:%08x:%08x:%08x dev(%s) on entry(%d), \n",
267 							event_cache[latest_cache_index].prefix_cache.v6Mask[0],
268 							event_cache[latest_cache_index].prefix_cache.v6Mask[1],
269 							event_cache[latest_cache_index].prefix_cache.v6Mask[2],
270 							event_cache[latest_cache_index].prefix_cache.v6Mask[3],
271 							event_cache[latest_cache_index].dev_name,
272 							latest_cache_index);
273 					}
274 					latest_cache_index = (latest_cache_index + 1)% MAX_EVENT_CACHE;
275 					break;
276 				}
277 				latest_cache_index = (latest_cache_index + 1)% MAX_EVENT_CACHE;
278 			}
279 			if(i == MAX_EVENT_CACHE - 1)
280 			{
281 				IPACMDBG_H(" run out of event cache (%d)\n", i);
282 				return FAIL_HARDWARE;
283 			}
284 		}
285 
286 		return SUCCESS;
287 	}
288 
289 	evt_data = (ipacm_event_ipahal_stream*)malloc(sizeof(ipacm_event_ipahal_stream));
290 	if(evt_data == NULL)
291 	{
292 		IPACMERR("Failed to allocate memory.\n");
293 		return FAIL_HARDWARE;
294 	}
295 	memset(evt_data, 0, sizeof(*evt_data));
296 
297 	evt_data->if_index = index;
298 	memcpy(&evt_data->prefix, &prefix, sizeof(evt_data->prefix));
299 
300 	memset(&evt, 0, sizeof(ipacm_cmd_q_data));
301 	evt.evt_data = (void*)evt_data;
302 	evt.event = IPA_DOWNSTREAM_ADD;
303 
304 	IPACMDBG_H("Posting event IPA_DOWNSTREAM_ADD\n");
305 	IPACM_EvtDispatcher::PostEvt(&evt);
306 
307 	return SUCCESS;
308 }
309 
removeDownstream(const char * downstream_name,const Prefix & prefix)310 RET IPACM_OffloadManager::removeDownstream(const char * downstream_name, const Prefix &prefix)
311 {
312 	int index;
313 	ipacm_cmd_q_data evt;
314 	ipacm_event_ipahal_stream *evt_data;
315 
316 	IPACMDBG_H("removeDownstream name(%s), ip-family(%d) \n", downstream_name, prefix.fam);
317 	if(strnlen(downstream_name, sizeof(downstream_name)) == 0)
318 	{
319 		IPACMERR("iface length is 0.\n");
320 		return FAIL_HARDWARE;
321 	}
322 	if (std::find(valid_ifaces.begin(), valid_ifaces.end(), std::string(downstream_name)) == valid_ifaces.end())
323 	{
324 		IPACMERR("iface is not present in list.\n");
325 		return FAIL_HARDWARE;
326 	}
327 
328 	if(ipa_get_if_index(downstream_name, &index))
329 	{
330 		IPACMERR("netdev(%s) already removed, ignored\n", downstream_name);
331 		return SUCCESS;
332 	}
333 
334 	evt_data = (ipacm_event_ipahal_stream*)malloc(sizeof(ipacm_event_ipahal_stream));
335 	if(evt_data == NULL)
336 	{
337 		IPACMERR("Failed to allocate memory.\n");
338 		return FAIL_HARDWARE;
339 	}
340 	memset(evt_data, 0, sizeof(*evt_data));
341 
342 	evt_data->if_index = index;
343 	memcpy(&evt_data->prefix, &prefix, sizeof(evt_data->prefix));
344 
345 	memset(&evt, 0, sizeof(ipacm_cmd_q_data));
346 	evt.evt_data = (void*)evt_data;
347 	evt.event = IPA_DOWNSTREAM_DEL;
348 
349 	IPACMDBG_H("Posting event IPA_DOWNSTREAM_DEL\n");
350 	IPACM_EvtDispatcher::PostEvt(&evt);
351 
352 	return SUCCESS;
353 }
354 
setUpstream(const char * upstream_name,const Prefix & gw_addr_v4,const Prefix & gw_addr_v6)355 RET IPACM_OffloadManager::setUpstream(const char *upstream_name, const Prefix& gw_addr_v4 , const Prefix& gw_addr_v6)
356 {
357 	int index;
358 	RET result = SUCCESS;
359 	bool cache_need = false;
360 
361 	/* if interface name is NULL, default route is removed */
362 	if(upstream_name != NULL)
363 	{
364 		IPACMDBG_H("setUpstream upstream_name(%s), ipv4-fam(%d) ipv6-fam(%d)\n", upstream_name, gw_addr_v4.fam, gw_addr_v6.fam);
365 	}
366 	else
367 	{
368 		IPACMDBG_H("setUpstream clean upstream_name for ipv4-fam(%d) ipv6-fam(%d)\n", gw_addr_v4.fam, gw_addr_v6.fam);
369 	}
370 	if(upstream_name == NULL)
371 	{
372 		if (default_gw_index == INVALID_IFACE) {
373 			result = FAIL_INPUT_CHECK;
374 			for (index = 0; index < MAX_EVENT_CACHE; index++) {
375 				if (event_cache[index].valid == true &&
376 					event_cache[index ].event == IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT) {
377 					event_cache[index].valid = false;
378 					result = SUCCESS;
379 				}
380 			}
381 			IPACMERR("no previous upstream set before\n");
382 			return result;
383 		}
384 		if (gw_addr_v4.fam == V4 && upstream_v4_up == true) {
385 			IPACMDBG_H("clean upstream for ipv4-fam(%d) upstream_v4_up(%d)\n", gw_addr_v4.fam, upstream_v4_up);
386 			post_route_evt(IPA_IP_v4, default_gw_index, IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT, gw_addr_v4);
387 			upstream_v4_up = false;
388 		}
389 		if (gw_addr_v6.fam == V6 && upstream_v6_up == true) {
390 			IPACMDBG_H("clean upstream for ipv6-fam(%d) upstream_v6_up(%d)\n", gw_addr_v6.fam, upstream_v6_up);
391 			post_route_evt(IPA_IP_v6, default_gw_index, IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT, gw_addr_v6);
392 			upstream_v6_up = false;
393 		}
394 		default_gw_index = INVALID_IFACE;
395 	}
396 	else
397 	{
398 		/* check if netdev valid on device */
399 		if(ipa_get_if_index(upstream_name, &index))
400 		{
401 			IPACMERR("fail to get iface index.\n");
402 			return FAIL_INPUT_CHECK;
403 		}
404 
405 		/* check if upstream netdev driver finished its configuration on IPA-HW for ipv4 and ipv6 */
406 		if (gw_addr_v4.fam == V4 && IPACM_Iface::ipacmcfg->CheckNatIfaces(upstream_name, IPA_IP_v4))
407 			cache_need = true;
408 		if (gw_addr_v6.fam == V6 && IPACM_Iface::ipacmcfg->CheckNatIfaces(upstream_name, IPA_IP_v6))
409 			cache_need = true;
410 
411 		if (cache_need)
412 		{
413 			IPACMDBG_H("setUpstream name(%s) currently not support in ipa \n", upstream_name);
414 #ifdef FEATURE_IPACM_RESTART
415 			/* add ipacm restart support */
416 			push_iface_up(upstream_name, true);
417 #endif
418 			/* copy to the cache */
419 			for(int i = 0; i < MAX_EVENT_CACHE ;i++)
420 			{
421 				if (latest_cache_index >= 0)
422 				{
423 					if(event_cache[latest_cache_index].valid == false)
424 					{
425 						//do the copy
426 						event_cache[latest_cache_index].valid = true;
427 						event_cache[latest_cache_index].event = IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT;
428 						memcpy(event_cache[latest_cache_index].dev_name, upstream_name,
429 							sizeof(event_cache[latest_cache_index].dev_name));
430 						memcpy(&event_cache[latest_cache_index].prefix_cache, &gw_addr_v4,
431 							sizeof(event_cache[latest_cache_index].prefix_cache));
432 						memcpy(&event_cache[latest_cache_index].prefix_cache_v6, &gw_addr_v6,
433 							sizeof(event_cache[latest_cache_index].prefix_cache_v6));
434 						if (gw_addr_v4.fam == V4) {
435 							IPACMDBG_H("cache event(%d) ipv4 gateway: (%x) dev(%s) on entry (%d)\n",
436 								event_cache[latest_cache_index].event,
437 								event_cache[latest_cache_index].prefix_cache.v4Addr,
438 								event_cache[latest_cache_index].dev_name,
439 								latest_cache_index);
440 						}
441 
442 						if (gw_addr_v6.fam == V6)
443 						{
444 							IPACMDBG_H("cache event (%d) ipv6 gateway: %08x:%08x:%08x:%08x dev(%s) on entry(%d)\n",
445 								event_cache[latest_cache_index].event,
446 								event_cache[latest_cache_index].prefix_cache_v6.v6Addr[0],
447 								event_cache[latest_cache_index].prefix_cache_v6.v6Addr[1],
448 								event_cache[latest_cache_index].prefix_cache_v6.v6Addr[2],
449 								event_cache[latest_cache_index].prefix_cache_v6.v6Addr[3],
450 								event_cache[latest_cache_index].dev_name,
451 								latest_cache_index);
452 						}
453 						latest_cache_index = (latest_cache_index + 1)% MAX_EVENT_CACHE;
454 						break;
455 					}
456 					latest_cache_index = (latest_cache_index + 1)% MAX_EVENT_CACHE;
457 				}
458 				if(i == MAX_EVENT_CACHE - 1)
459 				{
460 					IPACMDBG_H(" run out of event cache (%d) \n", i);
461 					return FAIL_HARDWARE;
462 				}
463 			}
464 			return SUCCESS;
465 		}
466 
467 		/* reset the stats when switch from LTE->STA */
468 		if (index != default_gw_index) {
469 			IPACMDBG_H(" interface switched to %s\n", upstream_name);
470 			if (upstream_v4_up == true) {
471 				IPACMDBG_H("clean upstream for ipv4-fam(%d) upstream_v4_up(%d)\n", gw_addr_v4.fam, upstream_v4_up);
472 				post_route_evt(IPA_IP_v4, default_gw_index, IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT, gw_addr_v4);
473 				upstream_v4_up = false;
474 			}
475 			if (upstream_v6_up == true) {
476 				IPACMDBG_H("clean upstream for ipv6-fam(%d) upstream_v6_up(%d)\n", gw_addr_v6.fam, upstream_v6_up);
477 				post_route_evt(IPA_IP_v6, default_gw_index, IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT, gw_addr_v6);
478 				upstream_v6_up = false;
479 			}
480 			default_gw_index = INVALID_IFACE;
481 			if(memcmp(upstream_name, "wlan0", sizeof("wlan0")) == 0)
482 			{
483 				IPACMDBG_H("switch to STA mode, need reset wlan-fw stats\n");
484 				resetTetherStats(upstream_name);
485 			}
486 		}
487 
488 		if (gw_addr_v4.fam == V4 && gw_addr_v6.fam == V6) {
489 
490 			if (upstream_v4_up == false) {
491 				IPACMDBG_H("IPV4 gateway: 0x%x \n", gw_addr_v4.v4Addr);
492 				/* posting route add event for both IPv4 and IPv6 */
493 				post_route_evt(IPA_IP_v4, index, IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT, gw_addr_v4);
494 				upstream_v4_up = true;
495 			} else {
496 				IPACMDBG_H("already setupstream iface(%s) ipv4 previously\n", upstream_name);
497 			}
498 
499 			if (upstream_v6_up == false) {
500 				IPACMDBG_H("IPV6 gateway: %08x:%08x:%08x:%08x \n",
501 						gw_addr_v6.v6Addr[0], gw_addr_v6.v6Addr[1], gw_addr_v6.v6Addr[2], gw_addr_v6.v6Addr[3]);
502 				/* check v6-address valid or not */
503 				if((gw_addr_v6.v6Addr[0] == 0) && (gw_addr_v6.v6Addr[1] ==0) && (gw_addr_v6.v6Addr[2] == 0) && (gw_addr_v6.v6Addr[3] == 0))
504 				{
505 					IPACMDBG_H("Invliad ipv6-address, ignored v6-setupstream\n");
506 				} else {
507 					post_route_evt(IPA_IP_v6, index, IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT, gw_addr_v6);
508 					upstream_v6_up = true;
509 				}
510 			} else {
511 				IPACMDBG_H("already setupstream iface(%s) ipv6 previously\n", upstream_name);
512 			}
513 		} else if (gw_addr_v4.fam == V4 ) {
514 			IPACMDBG_H("check upstream_v6_up (%d) v4_up (%d), default gw index (%d)\n", upstream_v6_up, upstream_v4_up, default_gw_index);
515 			if (upstream_v6_up == true && default_gw_index != INVALID_IFACE ) {
516 				/* clean up previous V6 upstream event */
517 				IPACMDBG_H(" Post clean-up v6 default gw on iface %d\n", default_gw_index);
518 				post_route_evt(IPA_IP_v6, default_gw_index, IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT, gw_addr_v6);
519 				upstream_v6_up = false;
520 			}
521 
522 			if (upstream_v4_up == false) {
523 				IPACMDBG_H("IPV4 gateway: %x \n", gw_addr_v4.v4Addr);
524 				/* posting route add event for both IPv4 and IPv6 */
525 				post_route_evt(IPA_IP_v4, index, IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT, gw_addr_v4);
526 				upstream_v4_up = true;
527 			} else {
528 				IPACMDBG_H("already setupstream iface(%s) ipv4 previously\n", upstream_name);
529 				result = SUCCESS_DUPLICATE_CONFIG;
530 			}
531 		} else if (gw_addr_v6.fam == V6) {
532 			IPACMDBG_H("check upstream_v6_up (%d) v4_up (%d), default gw index (%d)\n", upstream_v6_up, upstream_v4_up, default_gw_index);
533 			if (upstream_v4_up == true && default_gw_index != INVALID_IFACE ) {
534 				/* clean up previous V4 upstream event */
535 				IPACMDBG_H(" Post clean-up v4 default gw on iface %d\n", default_gw_index);
536 				post_route_evt(IPA_IP_v4, default_gw_index, IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT, gw_addr_v4);
537 				upstream_v4_up = false;
538 			}
539 
540 			if (upstream_v6_up == false) {
541 				IPACMDBG_H("IPV6 gateway: %08x:%08x:%08x:%08x \n",
542 						gw_addr_v6.v6Addr[0], gw_addr_v6.v6Addr[1], gw_addr_v6.v6Addr[2], gw_addr_v6.v6Addr[3]);
543 				/* check v6-address valid or not */
544 				if((gw_addr_v6.v6Addr[0] == 0) && (gw_addr_v6.v6Addr[1] ==0) && (gw_addr_v6.v6Addr[2] == 0) && (gw_addr_v6.v6Addr[3] == 0))
545 				{
546 					IPACMDBG_H("Invliad ipv6-address, ignored v6-setupstream\n");
547 				} else {
548 					post_route_evt(IPA_IP_v6, index, IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT, gw_addr_v6);
549 					upstream_v6_up = true;
550 				}
551 			} else {
552 				IPACMDBG_H("already setupstream iface(%s) ipv6 previously\n", upstream_name);
553 				result = SUCCESS_DUPLICATE_CONFIG;
554 			}
555 		}
556 		default_gw_index = index;
557 		IPACMDBG_H("Change degault_gw netdev to (%s)\n", upstream_name);
558 	}
559 	return result;
560 }
561 
stopAllOffload()562 RET IPACM_OffloadManager::stopAllOffload()
563 {
564 	Prefix v4gw, v6gw;
565 	RET result = SUCCESS;
566 
567 	memset(&v4gw, 0, sizeof(v4gw));
568 	memset(&v6gw, 0, sizeof(v6gw));
569 	v4gw.fam = V4;
570 	v6gw.fam = V6;
571 	IPACMDBG_H("posting setUpstream(NULL), ipv4-fam(%d) ipv6-fam(%d)\n", v4gw.fam, v6gw.fam);
572 	result = setUpstream(NULL, v4gw, v6gw);
573 
574 	/* reset the event cache */
575 	default_gw_index = INVALID_IFACE;
576 	upstream_v4_up = false;
577 	upstream_v6_up = false;
578 	memset(event_cache, 0, MAX_EVENT_CACHE*sizeof(framework_event_cache));
579 	latest_cache_index = 0;
580 	valid_ifaces.clear();
581 	IPACM_OffloadManager::num_offload_v4_tethered_iface = 0;
582 	return result;
583 }
584 
setQuota(const char * upstream_name,uint64_t mb)585 RET IPACM_OffloadManager::setQuota(const char * upstream_name /* upstream */, uint64_t mb/* limit */)
586 {
587 	wan_ioctl_set_data_quota quota;
588 	int fd = -1, rc = 0, err_type = 0;
589 
590 	if ((fd = open(DEVICE_NAME, O_RDWR)) < 0)
591 	{
592 		IPACMERR("Failed opening %s.\n", DEVICE_NAME);
593 		return FAIL_HARDWARE;
594 	}
595 
596 	quota.quota_mbytes = mb;
597 	quota.set_quota = true;
598 
599     memset(quota.interface_name, 0, IFNAMSIZ);
600     if (strlcpy(quota.interface_name, upstream_name, IFNAMSIZ) >= IFNAMSIZ) {
601 		IPACMERR("String truncation occurred on upstream");
602 		close(fd);
603 		return FAIL_INPUT_CHECK;
604 	}
605 
606 	IPACMDBG_H("SET_DATA_QUOTA %s %llu\n", quota.interface_name, (long long)mb);
607 
608 	rc = ioctl(fd, WAN_IOC_SET_DATA_QUOTA, &quota);
609 
610 	if(rc != 0)
611 	{
612 		err_type = errno;
613 		close(fd);
614 		IPACMERR("IOCTL WAN_IOCTL_SET_DATA_QUOTA call failed: %s err_type: %d\n", strerror(err_type), err_type);
615 		if (err_type == ENODEV) {
616 			IPACMDBG_H("Invalid argument.\n");
617 			return FAIL_UNSUPPORTED;
618 		}
619 		else {
620 			return FAIL_TRY_AGAIN;
621 		}
622 	}
623 	close(fd);
624 	return SUCCESS;
625 }
626 
getStats(const char * upstream_name,bool reset,OffloadStatistics & offload_stats)627 RET IPACM_OffloadManager::getStats(const char * upstream_name /* upstream */,
628 		bool reset /* reset */, OffloadStatistics& offload_stats/* ret */)
629 {
630 	int fd = -1;
631 	wan_ioctl_query_tether_stats_all stats;
632 
633 	if ((fd = open(DEVICE_NAME, O_RDWR)) < 0) {
634         IPACMERR("Failed opening %s.\n", DEVICE_NAME);
635         return FAIL_HARDWARE;
636     }
637 
638     memset(&stats, 0, sizeof(stats));
639     if (strlcpy(stats.upstreamIface, upstream_name, IFNAMSIZ) >= IFNAMSIZ) {
640 		IPACMERR("String truncation occurred on upstream\n");
641 		close(fd);
642 		return FAIL_INPUT_CHECK;
643 	}
644 	stats.reset_stats = reset;
645 	stats.ipa_client = IPACM_CLIENT_MAX;
646 
647 	if (ioctl(fd, WAN_IOC_QUERY_TETHER_STATS_ALL, &stats) < 0) {
648         IPACMERR("IOCTL WAN_IOC_QUERY_TETHER_STATS_ALL call failed: %s \n", strerror(errno));
649 		close(fd);
650 		return FAIL_TRY_AGAIN;
651 	}
652 	/* feedback to IPAHAL*/
653 	offload_stats.tx = stats.tx_bytes;
654 	offload_stats.rx = stats.rx_bytes;
655 
656 	IPACMDBG_H("send getStats tx:%llu rx:%llu \n", (long long)offload_stats.tx, (long long)offload_stats.rx);
657 	close(fd);
658 	return SUCCESS;
659 }
660 
post_route_evt(enum ipa_ip_type iptype,int index,ipa_cm_event_id event,const Prefix & gw_addr)661 int IPACM_OffloadManager::post_route_evt(enum ipa_ip_type iptype, int index, ipa_cm_event_id event, const Prefix &gw_addr)
662 {
663 	ipacm_cmd_q_data evt;
664 	ipacm_event_data_iptype *evt_data_route;
665 
666 	evt_data_route = (ipacm_event_data_iptype*)malloc(sizeof(ipacm_event_data_iptype));
667 	if(evt_data_route == NULL)
668 	{
669 		IPACMERR("Failed to allocate memory.\n");
670 		return -EFAULT;
671 	}
672 	memset(evt_data_route, 0, sizeof(*evt_data_route));
673 
674 	evt_data_route->if_index = index;
675 	evt_data_route->if_index_tether = 0;
676 	evt_data_route->iptype = iptype;
677 
678 	IPACMDBG_H("gw_addr.v4Addr: %d, gw_addr.v6Addr: %08x:%08x:%08x:%08x \n",
679 			gw_addr.v4Addr,gw_addr.v6Addr[0],gw_addr.v6Addr[1],gw_addr.v6Addr[2],gw_addr.v6Addr[3]);
680 
681 #ifdef IPA_WAN_MSG_IPv6_ADDR_GW_LEN
682 	evt_data_route->ipv4_addr_gw = gw_addr.v4Addr;
683 	evt_data_route->ipv6_addr_gw[0] = gw_addr.v6Addr[0];
684 	evt_data_route->ipv6_addr_gw[1] = gw_addr.v6Addr[1];
685 	evt_data_route->ipv6_addr_gw[2] = gw_addr.v6Addr[2];
686 	evt_data_route->ipv6_addr_gw[3] = gw_addr.v6Addr[3];
687 	IPACMDBG_H("default gw ipv4 (%x)\n", evt_data_route->ipv4_addr_gw);
688 	IPACMDBG_H("IPV6 gateway: %08x:%08x:%08x:%08x \n",
689 					evt_data_route->ipv6_addr_gw[0], evt_data_route->ipv6_addr_gw[1], evt_data_route->ipv6_addr_gw[2], evt_data_route->ipv6_addr_gw[3]);
690 #endif
691 	if (event == IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT) {
692 		IPACMDBG_H("Received WAN_UPSTREAM_ROUTE_ADD: fid(%d) tether_fid(%d) ip-type(%d)\n", evt_data_route->if_index,
693 			evt_data_route->if_index_tether, evt_data_route->iptype);
694 	}
695 	else if (event == IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT) {
696 		IPACMDBG_H("Received WAN_UPSTREAM_ROUTE_DEL: fid(%d) tether_fid(%d) ip-type(%d)\n",
697 				evt_data_route->if_index,
698 				evt_data_route->if_index_tether, evt_data_route->iptype);
699 	}
700 	memset(&evt, 0, sizeof(evt));
701 	evt.evt_data = (void*)evt_data_route;
702 	evt.event = event;
703 
704 	IPACM_EvtDispatcher::PostEvt(&evt);
705 
706 	return 0;
707 }
708 
ipa_get_if_index(const char * if_name,int * if_index)709 int IPACM_OffloadManager::ipa_get_if_index(const char * if_name, int * if_index)
710 {
711 	int fd;
712 	struct ifreq ifr;
713 
714 	if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
715 	{
716 		IPACMERR("get interface index socket create failed \n");
717 		return IPACM_FAILURE;
718 	}
719 
720 	if(strnlen(if_name, sizeof(if_name)) >= sizeof(ifr.ifr_name)) {
721 		IPACMERR("interface name overflows: len %zu\n", strnlen(if_name, sizeof(if_name)));
722 		close(fd);
723 		return IPACM_FAILURE;
724 	}
725 
726 	memset(&ifr, 0, sizeof(struct ifreq));
727 	(void)strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
728 	IPACMDBG_H("interface name (%s)\n", if_name);
729 
730 	if(ioctl(fd,SIOCGIFINDEX , &ifr) < 0)
731 	{
732 		IPACMERR("call_ioctl_on_dev: ioctl failed, interface name (%s):\n", ifr.ifr_name);
733 		close(fd);
734 		return IPACM_FAILURE;
735 	}
736 
737 	*if_index = ifr.ifr_ifindex;
738 	IPACMDBG_H("Interface netdev index %d\n", *if_index);
739 	close(fd);
740 	return IPACM_SUCCESS;
741 }
742 
resetTetherStats(const char * upstream_name)743 int IPACM_OffloadManager::resetTetherStats(const char * upstream_name /* upstream */)
744 {
745 	int fd = -1;
746 	wan_ioctl_reset_tether_stats stats;
747 
748 	if ((fd = open(DEVICE_NAME, O_RDWR)) < 0) {
749         IPACMERR("Failed opening %s.\n", DEVICE_NAME);
750         return FAIL_HARDWARE;
751     }
752 
753     memset(stats.upstreamIface, 0, IFNAMSIZ);
754     if (strlcpy(stats.upstreamIface, upstream_name, IFNAMSIZ) >= IFNAMSIZ) {
755 		IPACMERR("String truncation occurred on upstream\n");
756 		close(fd);
757 		return FAIL_INPUT_CHECK;
758 	}
759 	stats.reset_stats = true;
760 	if (ioctl(fd, WAN_IOC_RESET_TETHER_STATS, &stats) < 0) {
761 		IPACMERR("IOCTL WAN_IOC_RESET_TETHER_STATS call failed: %s", strerror(errno));
762 		close(fd);
763 		return FAIL_HARDWARE;
764 	}
765 	IPACMDBG_H("Reset Interface %s stats\n", upstream_name);
766 	close(fd);
767 	return IPACM_SUCCESS;
768 }
769 
GetInstance()770 IPACM_OffloadManager* IPACM_OffloadManager::GetInstance()
771 {
772 	if(pInstance == NULL)
773 		pInstance = new IPACM_OffloadManager();
774 
775 	return pInstance;
776 }
777 
search_framwork_cache(char * interface_name)778 bool IPACM_OffloadManager::search_framwork_cache(char * interface_name)
779 {
780 	bool rel = false;
781 	bool cache_need = false;
782 
783 	/* IPACM needs to kee old FDs, can't clear */
784 	IPACMDBG_H("check netdev(%s)\n", interface_name);
785 
786 	for(int i = 0; i < MAX_EVENT_CACHE ;i++)
787 	{
788 		cache_need = false;
789 		if(event_cache[i].valid == true)
790 		{
791 			//do the compare
792 			if (strncmp(event_cache[i].dev_name,
793 					interface_name,
794 					sizeof(event_cache[i].dev_name)) == 0)
795 			{
796 				IPACMDBG_H("found netdev (%s) in entry (%d) with event (%d)\n", interface_name, i, event_cache[i].event);
797 				/* post event again */
798 				if (event_cache[i].event == IPA_DOWNSTREAM_ADD) {
799 					/* check if downsteam netdev driver finished its configuration on IPA-HW for ipv4 and ipv6 */
800 					if (event_cache[i].prefix_cache.fam == V4 && IPACM_Iface::ipacmcfg->CheckNatIfaces(event_cache[i].dev_name, IPA_IP_v4))
801 						cache_need = true;
802 					if (event_cache[i].prefix_cache.fam == V6 && IPACM_Iface::ipacmcfg->CheckNatIfaces(event_cache[i].dev_name, IPA_IP_v6))
803 						cache_need = true;
804 					if (cache_need) {
805 						IPACMDBG_H("still need cache (%d), index (%d) ip-family (%d)\n", cache_need, i, event_cache[i].prefix_cache.fam);
806 						break;
807 					} else {
808 						IPACMDBG_H("no need cache (%d), handling it event (%d)\n", cache_need, event_cache[i].event);
809 					addDownstream(interface_name, event_cache[i].prefix_cache);
810 					}
811 				} else if (event_cache[i].event == IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT) {
812 					/* check if upstream netdev driver finished its configuration on IPA-HW for ipv4 and ipv6 */
813 					if (event_cache[i].prefix_cache.fam == V4 && IPACM_Iface::ipacmcfg->CheckNatIfaces(event_cache[i].dev_name, IPA_IP_v4))
814 						cache_need = true;
815 					if (event_cache[i].prefix_cache_v6.fam == V6 && IPACM_Iface::ipacmcfg->CheckNatIfaces(event_cache[i].dev_name, IPA_IP_v6))
816 						cache_need = true;
817 					if (cache_need) {
818 						IPACMDBG_H("still need cache (%d), index (%d)\n", cache_need, i);
819 						break;
820 					} else {
821 						IPACMDBG_H("no need cache (%d), handling it event (%d)\n", cache_need, event_cache[i].event);
822 					setUpstream(interface_name, event_cache[i].prefix_cache, event_cache[i].prefix_cache_v6);
823 					}
824 				} else {
825 						IPACMERR("wrong event cached (%d) index (%d)\n", event_cache[i].event, i);
826 				}
827 
828 				/* reset entry */
829 				event_cache[i].valid = false;
830 				rel = true;
831 				IPACMDBG_H("reset entry (%d)", i);
832 			}
833 		}
834 	}
835 	IPACMDBG_H(" not found netdev (%s) has cached event\n", interface_name);
836 	return rel;
837 }
838 
839 #ifdef FEATURE_IPACM_RESTART
push_iface_up(const char * if_name,bool upstream)840 int IPACM_OffloadManager::push_iface_up(const char * if_name, bool upstream)
841 {
842 	ipacm_cmd_q_data evt_data;
843 	ipacm_event_data_fid *data_fid = NULL;
844 	ipacm_event_data_mac *data = NULL;
845 	int index;
846 
847 	IPACMDBG_H("name %s, upstream %d\n",
848 							 if_name, upstream);
849 
850 	if(ipa_get_if_index(if_name, &index))
851 	{
852 		IPACMERR("netdev(%s) not registered ignored\n", if_name);
853 		return SUCCESS;
854 	}
855 
856 	if(strncmp(if_name, "rmnet_data", 10) == 0 && upstream)
857 	{
858 		data_fid = (ipacm_event_data_fid *)malloc(sizeof(ipacm_event_data_fid));
859 		if(data_fid == NULL)
860 		{
861 			IPACMERR("unable to allocate memory for event data_fid\n");
862 			return FAIL_HARDWARE;
863 		}
864 		data_fid->if_index = index;
865 		evt_data.event = IPA_LINK_UP_EVENT;
866 		evt_data.evt_data = data_fid;
867 		IPACMDBG_H("Posting IPA_LINK_UP_EVENT with if index: %d\n",
868 							 data_fid->if_index);
869 		IPACM_EvtDispatcher::PostEvt(&evt_data);
870 	}
871 
872 	if(strncmp(if_name, "rndis", 5) == 0 && !upstream)
873 	{
874 		data_fid = (ipacm_event_data_fid *)malloc(sizeof(ipacm_event_data_fid));
875 		if(data_fid == NULL)
876 		{
877 			IPACMERR("unable to allocate memory for event data_fid\n");
878 			return FAIL_HARDWARE;
879 		}
880 		data_fid->if_index = index;
881 		evt_data.event = IPA_USB_LINK_UP_EVENT;
882 		evt_data.evt_data = data_fid;
883 		IPACMDBG_H("Posting usb IPA_LINK_UP_EVENT with if index: %d\n",
884 				data_fid->if_index);
885 		IPACM_EvtDispatcher::PostEvt(&evt_data);
886 	}
887 
888 	if((strncmp(if_name, "softap", 6) == 0 || strncmp(if_name, "wlan", 4) == 0 ) && !upstream)
889 	{
890 		data_fid = (ipacm_event_data_fid *)malloc(sizeof(ipacm_event_data_fid));
891 		if(data_fid == NULL)
892 		{
893 			IPACMERR("unable to allocate memory for event data_fid\n");
894 			return FAIL_HARDWARE;
895 		}
896 		data_fid->if_index = index;
897 		evt_data.event = IPA_WLAN_AP_LINK_UP_EVENT;
898 		evt_data.evt_data = data_fid;
899 		IPACMDBG_H("Posting IPA_WLAN_AP_LINK_UP_EVENT with if index: %d\n",
900 			data_fid->if_index);
901 		IPACM_EvtDispatcher::PostEvt(&evt_data);
902 	}
903 
904 	if(strncmp(if_name, "wlan", 4) == 0 && upstream)
905 	{
906 		data = (ipacm_event_data_mac *)malloc(sizeof(ipacm_event_data_mac));
907 		if(data == NULL)
908 		{
909 			IPACMERR("unable to allocate memory for event_wlan data\n");
910 			return FAIL_HARDWARE;
911 		}
912 		data->if_index = index;
913 		evt_data.event = IPA_WLAN_STA_LINK_UP_EVENT;
914 		evt_data.evt_data = data;
915 		IPACMDBG_H("Posting IPA_WLAN_STA_LINK_UP_EVENT with if index: %d\n",
916 			data->if_index);
917 		IPACM_EvtDispatcher::PostEvt(&evt_data);
918 	}
919 
920 	return IPACM_SUCCESS;
921 }
922 #endif
923 
924 
push_framework_event(const char * if_name,_ipacm_offload_prefix prefix)925 bool IPACM_OffloadManager::push_framework_event(const char * if_name, _ipacm_offload_prefix prefix)
926 {
927 	bool ret =  false;
928 
929 	for(int i = 0; i < MAX_EVENT_CACHE ;i++)
930 	{
931 		if((latest_cache_index >= 0) && (latest_cache_index < MAX_EVENT_CACHE) &&
932 			(event_cache[latest_cache_index].valid == false))
933 		{
934 			//do the copy
935 			event_cache[latest_cache_index].valid = true;
936 			event_cache[latest_cache_index].event = IPA_DOWNSTREAM_ADD;
937 			memcpy(event_cache[latest_cache_index].dev_name, if_name,
938 				sizeof(event_cache[latest_cache_index].dev_name));
939 			memcpy(&event_cache[latest_cache_index].prefix_cache, &prefix,
940 				sizeof(event_cache[latest_cache_index].prefix_cache));
941 
942 			if (prefix.iptype == IPA_IP_v4) {
943 				IPACMDBG_H("cache event(%d) subnet info v4Addr (%x) v4Mask (%x) dev(%s) on entry (%d)\n",
944 						event_cache[latest_cache_index].event,
945 						event_cache[latest_cache_index].prefix_cache.v4Addr,
946 						event_cache[latest_cache_index].prefix_cache.v4Mask,
947 						event_cache[latest_cache_index].dev_name,
948 						latest_cache_index);
949 			} else {
950 				IPACMDBG_H("cache event (%d) v6Addr: %08x:%08x:%08x:%08x \n",
951 						event_cache[latest_cache_index].event,
952 						event_cache[latest_cache_index].prefix_cache.v6Addr[0],
953 						event_cache[latest_cache_index].prefix_cache.v6Addr[1],
954 						event_cache[latest_cache_index].prefix_cache.v6Addr[2],
955 						event_cache[latest_cache_index].prefix_cache.v6Addr[3]);
956 				IPACMDBG_H("subnet v6Mask: %08x:%08x:%08x:%08x dev(%s) on entry(%d),\n",
957 						event_cache[latest_cache_index].prefix_cache.v6Mask[0],
958 						event_cache[latest_cache_index].prefix_cache.v6Mask[1],
959 						event_cache[latest_cache_index].prefix_cache.v6Mask[2],
960 						event_cache[latest_cache_index].prefix_cache.v6Mask[3],
961 						event_cache[latest_cache_index].dev_name,
962 						latest_cache_index);
963 			}
964 			latest_cache_index = (latest_cache_index + 1)% MAX_EVENT_CACHE;
965 			ret = true;
966 			break;
967 		}
968 		latest_cache_index = (latest_cache_index + 1)% MAX_EVENT_CACHE;
969 		if(i == MAX_EVENT_CACHE - 1)
970 		{
971 			IPACMDBG_H(" run out of event cache (%d)\n", i);
972 			ret = false;
973 		}
974 	}
975 	return ret;
976 }
977