5 #import twisted.web.server import Site
6 #from twisted.web.resource import Resource
7 from twisted
.web
.server
import NOT_DONE_YET
8 from twisted
.internet
import reactor
10 from optparse
import OptionParser
11 from configparser
import ConfigParser
12 from configparser
import NoOptionError
23 r
= ipaddress
.IPv4Address(input)
24 except AddressValueError
:
25 r
= ipaddress
.IPv6Address(input)
30 r
= ipaddress
.IPv4Network(input)
31 except NetworkValueError
:
32 r
= ipaddress
.IPv6Network(input)
37 max_batch_down = 65536
48 ipif = userv root ipif %(host),%(relay),%(mtu),slip %(network)
53 max_batch_down = 262144
55 max_request_time = 121
58 #---------- "router" ----------
60 def route(packet
. daddr
):
61 try: client
= clients
[daddr
]
62 except KeyError: dclient
= None
63 if dclient
is not None:
64 dclient
.queue_outbound(packet
)
65 else if daddr
= host
or daddr
not in network
:
67 else if daddr
= relay
:
68 log_discard(packet
, saddr
, daddr
, 'relay')
70 log_discard(packet
, saddr
, daddr
, 'no client')
72 def log_discard(packet
, saddr
, daddr
, why
):
73 syslog
.syslog(syslog
.LOG_DEBUG
,
74 'discarded packet %s -> %s (%s)' %
(saddr
, daddr
, why
))
76 #---------- ipif (slip subprocess) ----------
78 class IpifProcessProtocol(twisted
.internet
.protocol
.ProcessProtocol
):
81 def connectionMade(self
): pass
82 def outReceived(self
, data
):
84 packets
= slip_decode(buffer)
85 buffer = packets
.pop()
86 for packet
in packets
:
87 (saddr
, daddr
) = packet_addrs(packet
)
89 def processEnded(self
, status
):
90 status
.raiseException()
94 ipif
= IpifProcessProtocol()
95 reactor
.spawnProcess(ipif
,
96 '/bin/sh',['-c', ipif_command
],
97 childFDs
={0:'w', 1:'r', 2:2})
99 def queue_inbound(packet
):
100 ipif
.transport
.write(slip_delimiter
)
101 ipif
.transport
.write(slip_encode(packet
))
102 ipif
.transport
.write(slip_delimiter
)
104 #---------- client ----------
107 def __init__(self
, ip
, cs
):
108 # instance data members
111 self
.pw
= cfg
.get(cs
, 'password')
112 self
._rq
= collections
.deque() # requests
113 self
._pq
= collections
.deque() # packets
118 for k
in ('max_batch_down','max_queue_time','max_request_time'):
119 req
= cfg
.getint(cs
, k
)
120 limit
= cfg
.getint('limits',k
)
121 self
.__dict__
[k
] = min(req
, limit
)
123 def process_arriving_data(self
, d
):
124 for packet
in slip_decode(d
):
125 (saddr
, daddr
) = packet_addrs(packet
)
126 if saddr
!= self
._ip
:
127 raise ValueError('wrong source address %s' % saddr
)
130 def _req_cancel(self
, request
):
133 def _req_error(self
, err
, request
):
134 self
._req_cancel(request
)
136 def queue_outbound(self
, packet
):
137 self
._pq
.append((time
.monotonic(), packet
))
139 def http_request(self
, request
):
140 request
.setHeader('Content-Type','application/octet-stream')
141 reactor
.callLater(self
.max_request_time
, self
._req_cancel
, request
)
142 request
.notifyFinish().addErrback(self
._req_error
, request
)
143 self
._rq
.append(request
)
144 self
._check_outbound()
146 def _check_outbound(self
):
148 try: request
= self
._rq
[0]
149 except IndexError: request
= None
150 if request
and request
.finished
:
154 # now request is an unfinished request, or None
155 try: (queuetime
, packet
) = self
._pq
[0]
157 # no packets, oh well
160 age
= time
.monotonic() - queuetime
161 if age
> self
.max_queue_time
:
169 # request, and also some non-expired packets
171 try: (dummy
, packet
) = self
._pq
[0]
172 except IndexError: break
174 encoded
= slip_encode(packet
)
176 if request
.sentLength
> 0:
177 if (request
.sentLength
+ len(slip_delimiter
)
178 + len(encoded
) > self
.max_batch_down
):
180 request
.write(slip_delimiter
)
182 request
.write(encoded
)
185 assert(request
.sentLength
)
188 # round again, looking for more to do
190 class IphttpResource(twisted
.web
.resource
.Resource
):
191 def render_POST(self
, request
):
192 # find client, update config, etc.
193 ci
= ipaddress(request
.args
['i'])
195 pw
= request
.args
['pw']
196 if pw
!= c
.pw
: raise ValueError('bad password')
199 for r
, w
in (('mbd', 'max_batch_down'),
200 ('mqt', 'max_queue_time'),
201 ('mrt', 'max_request_time')):
202 try: v
= request
.args
[r
]
203 except KeyError: continue
207 try: d
= request
.args
['d']
208 except KeyError: d
= ''
210 c
.process_arriving_data(d
)
211 c
.new_request(request
)
214 resource
= IphttpResource()
215 sitefactory
= twisted
.web
.server
.Site(resource
)
216 for addrspec
in cfg
.get('server','addresses').split():
218 addr
= ipaddress
.IPv4Address(addrspec
)
219 endpointfactory
= twisted
.internet
.endpoints
.TCP4ServerEndpoint
220 except AddressValueError
:
221 addr
= ipaddress
.IPv6Address(addrspec
)
222 endpointfactory
= twisted
.internet
.endpoints
.TCP6ServerEndpoint
223 ep
= endpointfactory(reactor
, cfg
.getint('server','port'), addr
)
224 ep
.listen(sitefactory
)
226 #---------- config and setup ----------
234 network
= ipnetwork(cfg
.get('virtual','network'))
235 if network
.num_addresses
< 3 + 2:
236 raise ValueError('network needs at least 2^3 addresses')
239 host
= cfg
.get('virtual','host')
240 except NoOptionError
:
241 host
= network
.hosts().next()
244 relay
= cfg
.get('virtual','relay')
246 for search
in network
.hosts():
247 if search
= host
: continue
251 for cs
in cfg
.sections():
252 if not (':' in cs
or '.' in cs
): continue
254 if ci
not in network
:
255 raise ValueError('client %s not in network' % ci
)
257 raise ValueError('multiple client cfg sections for %s' % ci
)
258 clients
[ci
] = Client(ci
, cs
)
261 for k
in ('host','relay','mtu','network'):
262 iic_vars
[k
] = globals()[k
]
264 ipif_command
= cfg
.get('server','ipif', vars=iic_vars
)
268 op
.add_option('-c', '--config', dest
='configfile',
269 default
='/etc/hippottd/server.conf')
271 (opts
, args
) = op
.parse_args()
272 if len(args
): op
.error('no non-option arguments please')
275 cfg
.read_string(defcfg
)
276 cfg
.read_file(opts
['configfile'])