Contiki 3.x
rpl-mrhof.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010, Swedish Institute of Computer Science.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 /**
34  * \file
35  * The Minimum Rank with Hysteresis Objective Function (MRHOF), RFC6719
36  *
37  * This implementation uses the estimated number of
38  * transmissions (ETX) as the additive routing metric,
39  * and also provides stubs for the energy metric.
40  *
41  * \author Joakim Eriksson <joakime@sics.se>, Nicolas Tsiftes <nvt@sics.se>
42  */
43 
44 /**
45  * \addtogroup uip6
46  * @{
47  */
48 
49 #include "net/rpl/rpl.h"
50 #include "net/rpl/rpl-private.h"
51 #include "net/nbr-table.h"
52 #include "net/link-stats.h"
53 
54 #define DEBUG DEBUG_NONE
55 #include "net/ip/uip-debug.h"
56 
57 /* RFC6551 and RFC6719 do not mandate the use of a specific formula to
58  * compute the ETX value. This MRHOF implementation relies on the value
59  * computed by the link-stats module. It has an optional feature,
60  * RPL_MRHOF_CONF_SQUARED_ETX, that consists in squaring this value.
61  * This basically penalizes bad links while preserving the semantics of ETX
62  * (1 = perfect link, more = worse link). As a result, MRHOF will favor
63  * good links over short paths. Recommended when reliability is a priority.
64  * Without this feature, a hop with 50% PRR (ETX=2) is equivalent to two
65  * perfect hops with 100% PRR (ETX=1+1=2). With this feature, the former
66  * path obtains ETX=2*2=4 and the former ETX=1*1+1*1=2. */
67 #ifdef RPL_MRHOF_CONF_SQUARED_ETX
68 #define RPL_MRHOF_SQUARED_ETX RPL_MRHOF_CONF_SQUARED_ETX
69 #else /* RPL_MRHOF_CONF_SQUARED_ETX */
70 #define RPL_MRHOF_SQUARED_ETX 0
71 #endif /* RPL_MRHOF_CONF_SQUARED_ETX */
72 
73 #if !RPL_MRHOF_SQUARED_ETX
74 /* Configuration parameters of RFC6719. Reject parents that have a higher
75  * link metric than the following. The default value is 512 but we use 1024. */
76 #define MAX_LINK_METRIC 1024 /* Eq ETX of 8 */
77 /* Hysteresis of MRHOF: the rank must differ more than PARENT_SWITCH_THRESHOLD_DIV
78  * in order to switch preferred parent. Default in RFC6719: 192, eq ETX of 1.5.
79  * We use a more aggressive setting: 96, eq ETX of 0.75.
80  */
81 #define PARENT_SWITCH_THRESHOLD 96 /* Eq ETX of 0.75 */
82 #else /* !RPL_MRHOF_SQUARED_ETX */
83 #define MAX_LINK_METRIC 2048 /* Eq ETX of 4 */
84 #define PARENT_SWITCH_THRESHOLD 160 /* Eq ETX of 1.25 (results in a churn comparable
85 to the threshold of 96 in the non-squared case) */
86 #endif /* !RPL_MRHOF_SQUARED_ETX */
87 
88 /* Reject parents that have a higher path cost than the following. */
89 #define MAX_PATH_COST 32768 /* Eq path ETX of 256 */
90 
91 /*---------------------------------------------------------------------------*/
92 static void
93 reset(rpl_dag_t *dag)
94 {
95  PRINTF("RPL: Reset MRHOF\n");
96 }
97 /*---------------------------------------------------------------------------*/
98 #if RPL_WITH_DAO_ACK
99 static void
100 dao_ack_callback(rpl_parent_t *p, int status)
101 {
102  if(status == RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT) {
103  return;
104  }
105  /* here we need to handle failed DAO's and other stuff */
106  PRINTF("RPL: MRHOF - DAO ACK received with status: %d\n", status);
107  if(status >= RPL_DAO_ACK_UNABLE_TO_ACCEPT) {
108  /* punish the ETX as if this was 10 packets lost */
109  link_stats_packet_sent(rpl_get_parent_lladdr(p), MAC_TX_OK, 10);
110  } else if(status == RPL_DAO_ACK_TIMEOUT) { /* timeout = no ack */
111  /* punish the total lack of ACK with a similar punishment */
112  link_stats_packet_sent(rpl_get_parent_lladdr(p), MAC_TX_OK, 10);
113  }
114 }
115 #endif /* RPL_WITH_DAO_ACK */
116 /*---------------------------------------------------------------------------*/
117 static uint16_t
118 parent_link_metric(rpl_parent_t *p)
119 {
120  const struct link_stats *stats = rpl_get_parent_link_stats(p);
121  if(stats != NULL) {
122 #if RPL_MRHOF_SQUARED_ETX
123  uint32_t squared_etx = ((uint32_t)stats->etx * stats->etx) / LINK_STATS_ETX_DIVISOR;
124  return (uint16_t)MIN(squared_etx, 0xffff);
125 #else /* RPL_MRHOF_SQUARED_ETX */
126  return stats->etx;
127 #endif /* RPL_MRHOF_SQUARED_ETX */
128  }
129  return 0xffff;
130 }
131 /*---------------------------------------------------------------------------*/
132 static uint16_t
133 parent_path_cost(rpl_parent_t *p)
134 {
135  uint16_t base;
136 
137  if(p == NULL || p->dag == NULL || p->dag->instance == NULL) {
138  return 0xffff;
139  }
140 
141 #if RPL_WITH_MC
142  /* Handle the different MC types */
143  switch(p->dag->instance->mc.type) {
144  case RPL_DAG_MC_ETX:
145  base = p->mc.obj.etx;
146  break;
147  case RPL_DAG_MC_ENERGY:
148  base = p->mc.obj.energy.energy_est << 8;
149  break;
150  default:
151  base = p->rank;
152  break;
153  }
154 #else /* RPL_WITH_MC */
155  base = p->rank;
156 #endif /* RPL_WITH_MC */
157 
158  /* path cost upper bound: 0xffff */
159  return MIN((uint32_t)base + parent_link_metric(p), 0xffff);
160 }
161 /*---------------------------------------------------------------------------*/
162 static rpl_rank_t
163 rank_via_parent(rpl_parent_t *p)
164 {
165  uint16_t min_hoprankinc;
166  uint16_t path_cost;
167 
168  if(p == NULL || p->dag == NULL || p->dag->instance == NULL) {
169  return INFINITE_RANK;
170  }
171 
172  min_hoprankinc = p->dag->instance->min_hoprankinc;
173  path_cost = parent_path_cost(p);
174 
175  /* Rank lower-bound: parent rank + min_hoprankinc */
176  return MAX(MIN((uint32_t)p->rank + min_hoprankinc, 0xffff), path_cost);
177 }
178 /*---------------------------------------------------------------------------*/
179 static int
180 parent_is_acceptable(rpl_parent_t *p)
181 {
182  uint16_t link_metric = parent_link_metric(p);
183  uint16_t path_cost = parent_path_cost(p);
184  /* Exclude links with too high link metrics or path cost (RFC6719, 3.2.2) */
185  return link_metric <= MAX_LINK_METRIC && path_cost <= MAX_PATH_COST;
186 }
187 /*---------------------------------------------------------------------------*/
188 static int
189 parent_has_usable_link(rpl_parent_t *p)
190 {
191  uint16_t link_metric = parent_link_metric(p);
192  /* Exclude links with too high link metrics */
193  return link_metric <= MAX_LINK_METRIC;
194 }
195 /*---------------------------------------------------------------------------*/
196 static rpl_parent_t *
197 best_parent(rpl_parent_t *p1, rpl_parent_t *p2)
198 {
199  rpl_dag_t *dag;
200  uint16_t p1_cost;
201  uint16_t p2_cost;
202  int p1_is_acceptable;
203  int p2_is_acceptable;
204 
205  p1_is_acceptable = p1 != NULL && parent_is_acceptable(p1);
206  p2_is_acceptable = p2 != NULL && parent_is_acceptable(p2);
207 
208  if(!p1_is_acceptable) {
209  return p2_is_acceptable ? p2 : NULL;
210  }
211  if(!p2_is_acceptable) {
212  return p1_is_acceptable ? p1 : NULL;
213  }
214 
215  dag = p1->dag; /* Both parents are in the same DAG. */
216  p1_cost = parent_path_cost(p1);
217  p2_cost = parent_path_cost(p2);
218 
219  /* Maintain stability of the preferred parent in case of similar ranks. */
220  if(p1 == dag->preferred_parent || p2 == dag->preferred_parent) {
221  if(p1_cost < p2_cost + PARENT_SWITCH_THRESHOLD &&
222  p1_cost > p2_cost - PARENT_SWITCH_THRESHOLD) {
223  return dag->preferred_parent;
224  }
225  }
226 
227  return p1_cost < p2_cost ? p1 : p2;
228 }
229 /*---------------------------------------------------------------------------*/
230 static rpl_dag_t *
231 best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
232 {
233  if(d1->grounded != d2->grounded) {
234  return d1->grounded ? d1 : d2;
235  }
236 
237  if(d1->preference != d2->preference) {
238  return d1->preference > d2->preference ? d1 : d2;
239  }
240 
241  return d1->rank < d2->rank ? d1 : d2;
242 }
243 /*---------------------------------------------------------------------------*/
244 #if !RPL_WITH_MC
245 static void
246 update_metric_container(rpl_instance_t *instance)
247 {
248  instance->mc.type = RPL_DAG_MC_NONE;
249 }
250 #else /* RPL_WITH_MC */
251 static void
252 update_metric_container(rpl_instance_t *instance)
253 {
254  rpl_dag_t *dag;
255  uint16_t path_cost;
256  uint8_t type;
257 
258  dag = instance->current_dag;
259  if(dag == NULL || !dag->joined) {
260  PRINTF("RPL: Cannot update the metric container when not joined\n");
261  return;
262  }
263 
264  if(dag->rank == ROOT_RANK(instance)) {
265  /* Configure MC at root only, other nodes are auto-configured when joining */
266  instance->mc.type = RPL_DAG_MC;
267  instance->mc.flags = 0;
268  instance->mc.aggr = RPL_DAG_MC_AGGR_ADDITIVE;
269  instance->mc.prec = 0;
270  path_cost = dag->rank;
271  } else {
272  path_cost = parent_path_cost(dag->preferred_parent);
273  }
274 
275  /* Handle the different MC types */
276  switch(instance->mc.type) {
277  case RPL_DAG_MC_NONE:
278  break;
279  case RPL_DAG_MC_ETX:
280  instance->mc.length = sizeof(instance->mc.obj.etx);
281  instance->mc.obj.etx = path_cost;
282  break;
283  case RPL_DAG_MC_ENERGY:
284  instance->mc.length = sizeof(instance->mc.obj.energy);
285  if(dag->rank == ROOT_RANK(instance)) {
286  type = RPL_DAG_MC_ENERGY_TYPE_MAINS;
287  } else {
288  type = RPL_DAG_MC_ENERGY_TYPE_BATTERY;
289  }
290  instance->mc.obj.energy.flags = type << RPL_DAG_MC_ENERGY_TYPE;
291  /* Energy_est is only one byte, use the least significant byte of the path metric. */
292  instance->mc.obj.energy.energy_est = path_cost >> 8;
293  break;
294  default:
295  PRINTF("RPL: MRHOF, non-supported MC %u\n", instance->mc.type);
296  break;
297  }
298 }
299 #endif /* RPL_WITH_MC */
300 /*---------------------------------------------------------------------------*/
301 rpl_of_t rpl_mrhof = {
302  reset,
303 #if RPL_WITH_DAO_ACK
304  dao_ack_callback,
305 #endif
306  parent_link_metric,
307  parent_has_usable_link,
308  parent_path_cost,
309  rank_via_parent,
310  best_parent,
311  best_dag,
312  update_metric_container,
313  RPL_OCP_MRHOF
314 };
315 
316 /** @}*/
The MAC layer transmission was OK.
Definition: mac.h:79
#define NULL
The null pointer.
A set of debugging macros for the IP stack