1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
|
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
from data import MessageRA, MessageMAR, MessageMAU
try:
from dispatch import *
except ImportError:
from ..stubs import *
class MobileAddressEngine(object):
"""
This module is responsible for maintaining an up-to-date list of mobile addresses in the domain.
It runs the Mobile-Address protocol and generates an un-optimized routing table for mobile addresses.
Note that this routing table maps from the mobile address to the remote router where that address
is directly bound.
"""
def __init__(self, container):
self.container = container
self.id = self.container.id
self.area = self.container.area
self.mobile_addr_max_age = self.container.config.mobile_addr_max_age
self.mobile_seq = 0
self.local_keys = []
self.added_keys = []
self.deleted_keys = []
self.remote_lists = {} # map router_id => (sequence, list of keys)
self.remote_last_seen = {} # map router_id => time of last seen advertizement/update
self.remote_changed = False
self.needed_mars = {}
def tick(self, now):
self._expire_remotes(now)
self._send_mars()
##
## If local keys have changed, collect the changes and send a MAU with the diffs
## Note: it is important that the differential-MAU be sent before a RA is sent
##
if len(self.added_keys) > 0 or len(self.deleted_keys) > 0:
self.mobile_seq += 1
self.container.send('_topo.%s.all' % self.area,
MessageMAU(None, self.id, self.area, self.mobile_seq, self.added_keys, self.deleted_keys))
self.local_keys.extend(self.added_keys)
for key in self.deleted_keys:
self.local_keys.remove(key)
self.added_keys = []
self.deleted_keys = []
self.container.mobile_sequence_changed(self.mobile_seq)
##
## If remotes have changed, start the process of updating local bindings
##
if self.remote_changed:
self.remote_changed = False
self._update_remote_keys()
def add_local_address(self, key):
"""
"""
if self.local_keys.count(key) == 0:
if self.added_keys.count(key) == 0:
self.added_keys.append(key)
else:
if self.deleted_keys.count(key) > 0:
self.deleted_keys.remove(key)
def del_local_address(self, key):
"""
"""
if self.local_keys.count(key) > 0:
if self.deleted_keys.count(key) == 0:
self.deleted_keys.append(key)
else:
if self.added_keys.count(key) > 0:
self.added_keys.remove(key)
def handle_ra(self, msg, now):
if msg.id == self.id:
return
if msg.mobile_seq == 0:
return
if msg.id in self.remote_lists:
_seq, _list = self.remote_lists[msg.id]
self.remote_last_seen[msg.id] = now
if _seq < msg.mobile_seq:
self.needed_mars[(msg.id, msg.area, _seq)] = None
else:
self.needed_mars[(msg.id, msg.area, 0)] = None
def handle_mau(self, msg, now):
##
## If the MAU is differential, we can only use it if its sequence is exactly one greater
## than our stored sequence. If not, we will ignore the content and schedule a MAR.
##
## If the MAU is absolute, we can use it in all cases.
##
if msg.id == self.id:
return
if msg.exist_list:
##
## Absolute MAU
##
if msg.id in self.remote_lists:
_seq, _list = self.remote_lists[msg.id]
if _seq >= msg.mobile_seq: # ignore duplicates
return
self.remote_lists[msg.id] = (msg.mobile_seq, msg.exist_list)
self.remote_last_seen[msg.id] = now
self.remote_changed = True
else:
##
## Differential MAU
##
if msg.id in self.remote_lists:
_seq, _list = self.remote_lists[msg.id]
if _seq == msg.mobile_seq: # ignore duplicates
return
self.remote_last_seen[msg.id] = now
if _seq + 1 == msg.mobile_seq:
##
## This is one greater than our stored value, incorporate the deltas
##
if msg.add_list and msg.add_list.__class__ == list:
_list.extend(msg.add_list)
if msg.del_list and msg.del_list.__class__ == list:
for key in msg.del_list:
_list.remove(key)
self.remote_lists[msg.id] = (msg.mobile_seq, _list)
self.remote_changed = True
else:
self.needed_mars[(msg.id, msg.area, _seq)] = None
else:
self.needed_mars[(msg.id, msg.area, 0)] = None
def handle_mar(self, msg, now):
if msg.id == self.id:
return
if msg.have_seq < self.mobile_seq:
self.container.send('_topo.%s.%s' % (msg.area, msg.id),
MessageMAU(None, self.id, self.area, self.mobile_seq, None, None, self.local_keys))
def _update_remote_keys(self):
keys = {}
for _id,(seq,key_list) in self.remote_lists.items():
keys[_id] = key_list
self.container.mobile_keys_changed(keys)
def _expire_remotes(self, now):
for _id, t in self.remote_last_seen.items():
if now - t > self.mobile_addr_max_age:
self.remote_lists.pop(_id)
self.remote_last_seen.pop(_id)
self.remote_changed = True
def _send_mars(self):
for _id, _area, _seq in self.needed_mars.keys():
self.container.send('_topo.%s.%s' % (_area, _id), MessageMAR(None, self.id, self.area, _seq))
self.needed_mars = {}
|