peak 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/CONTRIBUTORS.md +13 -0
- data/README.rdoc +6 -5
- data/bin/peak +1 -1
- data/lib/peak.rb +49 -11
- data/lib/peak/app_info.rb +1 -1
- data/lib/peak/config_loader.rb +8 -8
- data/lib/peak/routing/route.rb +687 -0
- data/peak.conf.example +17 -3
- data/peak.gemspec +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df65f4faa4d3b4e36b51a9d6b4a5bba37bb2c992
|
4
|
+
data.tar.gz: 9b35a626888fe27afe140abe5dc0bef82d154ae7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 712d1e69d00909111a9bca843918276148103c3372b69f1b08ec61176195edbd31f936c12eef7f7960b5f73a3730094f1937431215fc4f0ae38a34ecbb9c2b4d
|
7
|
+
data.tar.gz: 8f3a5a14d1a7d28b2e16d1d6bde9df2d2a11b5a01d988d7ed0dcdf445fd56fd143a7d22aeb83cc34c615d2c28de7e9aac5f4c77a73359f6e1ae679b82181c7a0
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.0.2
|
4
|
+
|
5
|
+
* Added proper cleanup before exiting when a sigterm or sigint is received. This allows serial connections to be
|
6
|
+
shutdown and KISS mode on the TNC to be exited.
|
7
|
+
* Added a Domain-specific Language for the routing and mangling of frames.
|
8
|
+
* By default frames are now preemptively digipeated.
|
9
|
+
|
3
10
|
## 1.0.1
|
4
11
|
|
5
12
|
* Added beacon plugin to transmit beacon every 10 minutes.
|
data/CONTRIBUTORS.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
* Jeffrey Phillips Freeman (WI2ARD) - http://JeffreyFreeman.me
|
2
|
+
* Martin Murray (KD8LVZ)
|
3
|
+
* Paul McMillan - https://github.com/PaulMcMillan
|
4
|
+
* Russ Innes
|
5
|
+
* John Hogenmiller (KB3DFZ) - https://github.com/ytjohn
|
6
|
+
* Phil Gagnon N1HHG
|
7
|
+
* Ben Benesh - https://github.com/bbene
|
8
|
+
* Joe Goforth
|
9
|
+
* Rick Eason
|
10
|
+
* Jay Nugent
|
11
|
+
* Pete Loveall (AE5PL)
|
12
|
+
* darksidelemm - https://github.com/darksidelemm
|
13
|
+
* agmuino - https://github.com/agmuino
|
data/README.rdoc
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
= Peak
|
2
2
|
|
3
3
|
{<img src="https://badge.fury.io/rb/peak.svg" alt="Gem Version" />}[https://badge.fury.io/rb/peak]
|
4
|
+
{<img src="http://img.shields.io/badge/yard-docs-blue.svg" />}[http://www.rubydoc.info/github/Syncleus/peak/master]
|
4
5
|
|
5
6
|
Author:: Jeffrey Phillips Freeman, WI2ARD (freemo@gmail.com)
|
6
|
-
Copyright:: Copyright (c) 2016 Syncleus, Inc.
|
7
|
+
Copyright:: Copyright (c) 2016 - present Syncleus, Inc.
|
7
8
|
|
8
9
|
Peak is a full suite APRS client and reference implementation for the APEX protocol. APEX is a next generation APRS
|
9
10
|
based protocol. This repository represents the official application implementing APEX. It is a full-feature application
|
@@ -34,12 +35,12 @@ This is Free software: Apache License v2
|
|
34
35
|
|
35
36
|
== Development
|
36
37
|
|
37
|
-
After checking out the repo, run
|
38
|
+
After checking out the repo, run +bin/setup+ to install dependencies. You can also run +bin/console+ for an interactive
|
38
39
|
prompt that will allow you to experiment.
|
39
40
|
|
40
|
-
To install this gem onto your local machine, run
|
41
|
-
version number in
|
42
|
-
push git commits and tags, and push the
|
41
|
+
To install this gem onto your local machine, run <code>bundle exec rake install</code>. To release a new version, update the
|
42
|
+
version number in +app_info.rb+, and then run <code>bundle exec rake release</code>, which will create a git tag for the version,
|
43
|
+
push git commits and tags, and push the +.gem+ file to {rubygems.org}[https://rubygems.org].
|
43
44
|
|
44
45
|
== Links
|
45
46
|
|
data/bin/peak
CHANGED
@@ -14,7 +14,7 @@ class App
|
|
14
14
|
Peak::main
|
15
15
|
end
|
16
16
|
|
17
|
-
description 'Peak
|
17
|
+
description 'Peak is a reference implementation for the APEX protocol which is itself based on the APRS protocol.'
|
18
18
|
version Peak::VERSION
|
19
19
|
|
20
20
|
# Proxy to an OptionParser instance's on method
|
data/lib/peak.rb
CHANGED
@@ -1,12 +1,25 @@
|
|
1
1
|
require 'peak/echo'
|
2
2
|
require 'peak/config_loader'
|
3
3
|
require 'peak/plugin_loader'
|
4
|
+
require 'peak/routing/route'
|
4
5
|
|
5
6
|
module Peak
|
6
7
|
|
7
8
|
def self.main
|
8
9
|
config = find_config(true)
|
10
|
+
unless config
|
11
|
+
echo_colorized_error('Could not find a valid configuration file in any of the default locations')
|
12
|
+
return
|
13
|
+
end
|
14
|
+
|
9
15
|
port_map = init_port_map(config)
|
16
|
+
unless port_map
|
17
|
+
echo_colorized_error('Configuration could not be loaded, format was invalid.')
|
18
|
+
return
|
19
|
+
end
|
20
|
+
|
21
|
+
Signal.trap('INT') { throw :sig }
|
22
|
+
Signal.trap('TERM') { throw :sig }
|
10
23
|
|
11
24
|
active_plugins = {}
|
12
25
|
plugins = load_plugins
|
@@ -19,20 +32,45 @@ module Peak
|
|
19
32
|
end
|
20
33
|
|
21
34
|
# Handle any packets we read in.
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
35
|
+
catch (:sig) do
|
36
|
+
while true
|
37
|
+
something_read = false
|
38
|
+
port_map.values.each do |tnc_port|
|
39
|
+
frame = tnc_port.read
|
40
|
+
if frame
|
41
|
+
something_read = true
|
42
|
+
routed_frame = Routing::Route.handle_frame(frame, config, true, tnc_port.name)
|
43
|
+
if routed_frame
|
44
|
+
if routed_frame[:output_target]
|
45
|
+
port_map[routed_frame[:output_target]].write(routed_frame[:frame])
|
46
|
+
else
|
47
|
+
active_plugins.each_key do |plugin|
|
48
|
+
plugin.handle_packet(routed_frame[:frame], tnc_port)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
30
52
|
end
|
31
53
|
end
|
32
|
-
|
33
|
-
|
34
|
-
|
54
|
+
unless something_read
|
55
|
+
sleep(1)
|
56
|
+
end
|
35
57
|
end
|
36
58
|
end
|
59
|
+
|
60
|
+
puts
|
61
|
+
puts 'Shutdown signal caught, shutting down...'
|
62
|
+
|
63
|
+
# Let's cleanup some stuff before exiting.
|
64
|
+
active_plugins.keys.each do |plugin|
|
65
|
+
plugin.stop
|
66
|
+
end
|
67
|
+
active_plugins.values.each do |plugin_thread|
|
68
|
+
plugin_thread.join
|
69
|
+
end
|
70
|
+
port_map.values.each do |port|
|
71
|
+
port.close
|
72
|
+
end
|
73
|
+
|
74
|
+
puts 'Peak successfully shutdown.'
|
37
75
|
end
|
38
76
|
end
|
data/lib/peak/app_info.rb
CHANGED
data/lib/peak/config_loader.rb
CHANGED
@@ -41,7 +41,7 @@ module Peak
|
|
41
41
|
tnc_name = section_name.strip.split(' ')[1].strip
|
42
42
|
if tnc_name == 'IGATE'
|
43
43
|
echo_colorized_error('IGATE was used as the name for a TNC in the configuration, this name is reserved')
|
44
|
-
return
|
44
|
+
return nil
|
45
45
|
end
|
46
46
|
|
47
47
|
kiss_tnc = nil
|
@@ -50,7 +50,7 @@ module Peak
|
|
50
50
|
baud = section_content['baud']
|
51
51
|
kiss_tnc = Apex::AprsKiss.new(Kiss::KissSerial.new(com_port, baud))
|
52
52
|
else
|
53
|
-
return
|
53
|
+
return nil
|
54
54
|
end
|
55
55
|
|
56
56
|
if section_content.key?('kiss_init')
|
@@ -63,14 +63,14 @@ module Peak
|
|
63
63
|
kiss_tnc.connect
|
64
64
|
else
|
65
65
|
echo_colorized_error('Invalid configuration, value assigned to kiss_init was not recognized: ' + kiss_init_string)
|
66
|
-
return
|
66
|
+
return nil
|
67
67
|
end
|
68
68
|
else
|
69
69
|
kiss_tnc.connect
|
70
70
|
end
|
71
71
|
|
72
72
|
unless config_lookup_enforce(section_content, 'port_count')
|
73
|
-
return
|
73
|
+
return nil
|
74
74
|
end
|
75
75
|
|
76
76
|
port_count = section_content['port_count']
|
@@ -80,22 +80,22 @@ module Peak
|
|
80
80
|
port_section_name = 'PORT ' + port_name
|
81
81
|
|
82
82
|
unless config_lookup_enforce(config, port_section_name)
|
83
|
-
return
|
83
|
+
return nil
|
84
84
|
end
|
85
85
|
port_section = config[port_section_name]
|
86
86
|
|
87
87
|
unless config_lookup_enforce(port_section, 'identifier')
|
88
|
-
return
|
88
|
+
return nil
|
89
89
|
end
|
90
90
|
port_identifier = port_section['identifier']
|
91
91
|
|
92
92
|
unless config_lookup_enforce(port_section, 'net')
|
93
|
-
return
|
93
|
+
return nil
|
94
94
|
end
|
95
95
|
port_net = port_section['net']
|
96
96
|
|
97
97
|
unless config_lookup_enforce(port_section, 'tnc_port')
|
98
|
-
return
|
98
|
+
return nil
|
99
99
|
end
|
100
100
|
tnc_port = port_section['tnc_port']
|
101
101
|
|
@@ -0,0 +1,687 @@
|
|
1
|
+
module Peak
|
2
|
+
module Routing
|
3
|
+
class Rules
|
4
|
+
protected
|
5
|
+
def initialize(frame, config, frame_port, next_target=nil)
|
6
|
+
@frame = frame
|
7
|
+
@next_target = next_target
|
8
|
+
@config = config
|
9
|
+
@frame_port = frame_port
|
10
|
+
|
11
|
+
@port_info = {}
|
12
|
+
config.each do |section_name, section_content|
|
13
|
+
if section_name.start_with?('TNC ')
|
14
|
+
tnc_name = section_name.strip.split(' ')[1].strip
|
15
|
+
if tnc_name != 'IGATE'
|
16
|
+
port_count = section_content['port_count']
|
17
|
+
|
18
|
+
(1..port_count).each do |port|
|
19
|
+
port_name = tnc_name + '-' + port.to_s
|
20
|
+
port_section_name = 'PORT ' + port_name
|
21
|
+
port_section = config[port_section_name]
|
22
|
+
port_identifier = port_section['identifier']
|
23
|
+
port_net = port_section['net']
|
24
|
+
tnc_port = port_section['tnc_port']
|
25
|
+
|
26
|
+
old_paradigm = port_section['old_paradigm']
|
27
|
+
unless old_paradigm
|
28
|
+
old_paradigm = []
|
29
|
+
end
|
30
|
+
|
31
|
+
new_paradigm_all = port_section['new_paradigm']
|
32
|
+
new_paradigm = []
|
33
|
+
if new_paradigm_all and new_paradigm_all.length > 0
|
34
|
+
new_paradigm_all.each do |new_paradigm_one|
|
35
|
+
new_paradigm << {:target => new_paradigm_one['target'], :max_hops => new_paradigm_one['max_hops']}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
preemptive = port_section['preemptive']
|
40
|
+
|
41
|
+
@port_info[port_name] = {
|
42
|
+
:port_identifier => port_identifier,
|
43
|
+
:port_net => port_net,
|
44
|
+
:tnc_port => tnc_port,
|
45
|
+
:old_paradigm => old_paradigm,
|
46
|
+
:new_paradigm => new_paradigm,
|
47
|
+
:preemptive => preemptive
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def self.args_parser(*args)
|
57
|
+
if !args or !args.length or args.length <= 0
|
58
|
+
condition = args[0]
|
59
|
+
true_target = :pass
|
60
|
+
false_target = :pass
|
61
|
+
elsif !!args[0] == args[0] # if the first argument is a boolean
|
62
|
+
condition = args[0]
|
63
|
+
true_target = args.length >= 2 ? args[1] : :pass
|
64
|
+
false_target = args.length >= 3 ? args[2] : :pass
|
65
|
+
else
|
66
|
+
condition = true
|
67
|
+
true_target = args.length >= 1 ? args[0] : :pass
|
68
|
+
false_target = args.length >= 2 ? args[1] : :pass
|
69
|
+
end
|
70
|
+
|
71
|
+
next_target = condition ? true_target : false_target
|
72
|
+
|
73
|
+
{
|
74
|
+
:condition => condition,
|
75
|
+
:true_target => true_target,
|
76
|
+
:false_target => false_target,
|
77
|
+
:next_target => next_target
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
def do_next_target(next_target)
|
83
|
+
if next_target != :pass
|
84
|
+
@next_target = next_target
|
85
|
+
throw :new_target
|
86
|
+
end
|
87
|
+
return
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
def select_next_hop(hops)
|
92
|
+
select_future_hops(hops)[0]
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def select_future_hops(hops)
|
97
|
+
hops.select { |hop| !hop.end_with? '*' }
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def select_net_band_hops(hops)
|
102
|
+
hops.select { |hop| hop =~ /\d{1,4}M$/i }
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
def select_net_freq_hops(hops)
|
107
|
+
hops.select { |hop| hop =~ /\d{1,4}M\d{1,3}$/i }
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
def select_net_hops(hops)
|
112
|
+
hops.select { |hop| hop =~ /\d{1,4}M\d{0,3}$/i }
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
def net_freg_to_band_hops(hops)
|
117
|
+
select_net_hops(hops).map { |hop| hop.gsub(/M\d{0,3}$/i, 'M') }
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
def all_my_names
|
122
|
+
names = @port_info.values.map { |info| info[:port_identifier].upcase }
|
123
|
+
names += net_freg_to_band_hops(@port_info.values.map {|info| info[:port_net].upcase })
|
124
|
+
names += select_net_freq_hops(@port_info.values.map {|info| info[:port_net].upcase })
|
125
|
+
names.uniq
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
def self.array_upcase(things)
|
130
|
+
things.map {|s|
|
131
|
+
if !s.is_a? Regexp
|
132
|
+
s.upcase
|
133
|
+
else
|
134
|
+
s
|
135
|
+
end
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
def self.is_hop_old_paradigm?(hop, old_paradigm)
|
141
|
+
if old_paradigm.length <= 0
|
142
|
+
return false
|
143
|
+
end
|
144
|
+
|
145
|
+
old_paradigm = Rules.array_upcase(old_paradigm)
|
146
|
+
hop = hop.upcase
|
147
|
+
|
148
|
+
old_paradigm.each do |target|
|
149
|
+
if target.is_a? Regexp
|
150
|
+
if hop =~ target
|
151
|
+
return true
|
152
|
+
end
|
153
|
+
elsif hop == target
|
154
|
+
return true
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
false
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
def self.is_hop_new_paradigm?(hop, new_paradigm_all)
|
163
|
+
if new_paradigm_all.length <= 0
|
164
|
+
return false
|
165
|
+
end
|
166
|
+
|
167
|
+
hop = hop.upcase
|
168
|
+
unless hop =~ /[A-Z]*-\d{1,2}/
|
169
|
+
return false
|
170
|
+
end
|
171
|
+
hop_split = hop.split('-')
|
172
|
+
if !hop_split or hop_split.length != 2
|
173
|
+
return false
|
174
|
+
end
|
175
|
+
hop_target = hop_split[0]
|
176
|
+
hop_hops = hop_split[1].to_i
|
177
|
+
if hop_hops <= 0
|
178
|
+
return false
|
179
|
+
end
|
180
|
+
|
181
|
+
new_paradigm_all.each do |new_paradigm|
|
182
|
+
if new_paradigm[:target].is_a? Regexp
|
183
|
+
if hop_target =~ new_paradigm[:target]
|
184
|
+
return true
|
185
|
+
end
|
186
|
+
elsif hop_target == new_paradigm[:target].upcase
|
187
|
+
return true
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
false
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
def self.is_hop_identifier_me(hop, identifiers)
|
196
|
+
if identifiers.include? hop.upcase
|
197
|
+
true
|
198
|
+
else
|
199
|
+
false
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
protected
|
204
|
+
def filter(*args)
|
205
|
+
args = Rules.args_parser(*args)
|
206
|
+
do_next_target(args[:next_target])
|
207
|
+
end
|
208
|
+
|
209
|
+
protected
|
210
|
+
def consume_next_hop(*args)
|
211
|
+
args = Rules.args_parser(*args)
|
212
|
+
|
213
|
+
if has_next_hop? and args[:condition]
|
214
|
+
catch(:done) do
|
215
|
+
@frame[:path].each do |hop|
|
216
|
+
unless hop.end_with? '*'
|
217
|
+
hop << '*'
|
218
|
+
throw :done
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
do_next_target(args[:next_target])
|
225
|
+
end
|
226
|
+
|
227
|
+
private
|
228
|
+
def self.consume_new_paradigm(hop)
|
229
|
+
hop_split = hop.split('-')
|
230
|
+
target = hop_split[0]
|
231
|
+
hop_count = hop_split[1].to_i
|
232
|
+
hop_count -= 1
|
233
|
+
if hop_count > 0
|
234
|
+
hop.replace( target + '-' + hop_count.to_s )
|
235
|
+
else
|
236
|
+
hop.replace(target + '*')
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
protected
|
241
|
+
def route(*args, preemptive: nil)
|
242
|
+
args = Rules.args_parser(*args)
|
243
|
+
|
244
|
+
if has_next_hop? and args[:condition]
|
245
|
+
detected = false
|
246
|
+
|
247
|
+
port_name = @frame_port
|
248
|
+
port_info = @port_info[port_name]
|
249
|
+
|
250
|
+
if preemptive == nil
|
251
|
+
preemptive = port_info[:preemptive]
|
252
|
+
if preemptive == nil
|
253
|
+
preemptive = true
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
old_paradigm = nil
|
258
|
+
if port_info.key? :old_paradigm and port_info[:old_paradigm].length > 0
|
259
|
+
old_paradigm = Rules.array_upcase(port_info[:old_paradigm])
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
new_paradigm = nil
|
264
|
+
if port_info.key? :new_paradigm and port_info[:new_paradigm].length > 0
|
265
|
+
new_paradigm = port_info[:new_paradigm]
|
266
|
+
end
|
267
|
+
|
268
|
+
|
269
|
+
next_hop = select_next_hop(@frame[:path])
|
270
|
+
if all_my_names.include? next_hop.upcase or Rules.is_hop_old_paradigm?(next_hop, old_paradigm)
|
271
|
+
next_hop << '*'
|
272
|
+
elsif Rules.is_hop_new_paradigm?(next_hop, new_paradigm)
|
273
|
+
Rules.consume_new_paradigm(next_hop)
|
274
|
+
end
|
275
|
+
|
276
|
+
if preemptive
|
277
|
+
future_hops = select_future_hops(@frame[:path])
|
278
|
+
identifiers = @port_info.values.map { |info| info[:port_identifier].upcase }
|
279
|
+
future_hops.reverse.each do |hop|
|
280
|
+
if detected
|
281
|
+
hop << '*'
|
282
|
+
elsif identifiers.include? hop.upcase
|
283
|
+
hop << '*'
|
284
|
+
detected = true
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
do_next_target(args[:next_target])
|
291
|
+
end
|
292
|
+
|
293
|
+
protected
|
294
|
+
def seen?
|
295
|
+
identifiers = @port_info.values.map { |info| info[:port_identifier].upcase }
|
296
|
+
if identifiers.include? @frame[:source].upcase
|
297
|
+
return true
|
298
|
+
end
|
299
|
+
|
300
|
+
@frame[:path].each do |hop|
|
301
|
+
unless hop.end_with? '*'
|
302
|
+
return false
|
303
|
+
end
|
304
|
+
|
305
|
+
if identifiers.include? hop.upcase.chomp('*')
|
306
|
+
return true
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
false
|
311
|
+
end
|
312
|
+
|
313
|
+
protected
|
314
|
+
def destination_me?
|
315
|
+
identifiers = @port_info.values.map { |info| info[:port_identifier].upcase }
|
316
|
+
if identifiers.include? @frame[:destination].upcase
|
317
|
+
true
|
318
|
+
else
|
319
|
+
false
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
protected
|
324
|
+
def has_next_hop?
|
325
|
+
@frame[:path].each do |hop|
|
326
|
+
unless hop.end_with? '*'
|
327
|
+
return true
|
328
|
+
end
|
329
|
+
end
|
330
|
+
return false
|
331
|
+
end
|
332
|
+
|
333
|
+
protected
|
334
|
+
def next_hop_identifier_me?
|
335
|
+
unless has_next_hop?
|
336
|
+
return false
|
337
|
+
end
|
338
|
+
|
339
|
+
identifiers = @port_info.values.map { |info| info[:port_identifier].upcase }
|
340
|
+
Rules.is_hop_identifier_me(select_next_hop(@frame[:path]), identifiers)
|
341
|
+
end
|
342
|
+
|
343
|
+
protected
|
344
|
+
def future_hop_identifier_me?
|
345
|
+
unless has_next_hop?
|
346
|
+
return false
|
347
|
+
end
|
348
|
+
|
349
|
+
identifiers = @port_info.values.map { |info| info[:port_identifier].upcase }
|
350
|
+
if (identifiers & select_future_hops(Rules.array_upcase(@frame[:path]))).empty?
|
351
|
+
false
|
352
|
+
else
|
353
|
+
true
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
protected
|
358
|
+
def next_hop_net_band_me?
|
359
|
+
unless has_next_hop?
|
360
|
+
return false
|
361
|
+
end
|
362
|
+
|
363
|
+
net_bands = net_freg_to_band_hops(@port_info.values.map {|info| info[:port_net].upcase })
|
364
|
+
next_bands = select_net_band_hops([select_next_hop(@frame[:path]).upcase])
|
365
|
+
|
366
|
+
if next_bands.length <= 0 or net_bands.length <= 0
|
367
|
+
return false
|
368
|
+
end
|
369
|
+
|
370
|
+
next_band = next_bands[0]
|
371
|
+
|
372
|
+
if net_bands.include? next_band
|
373
|
+
false
|
374
|
+
else
|
375
|
+
true
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
protected
|
380
|
+
def future_hop_net_band_me?
|
381
|
+
unless has_next_hop?
|
382
|
+
return false
|
383
|
+
end
|
384
|
+
|
385
|
+
net_bands = net_freg_to_band_hops(@port_info.values.map {|info| info[:port_net].upcase })
|
386
|
+
next_bands = Rules.array_upcase(select_net_band_hops(@frame[:path]))
|
387
|
+
|
388
|
+
if next_bands.length <= 0 or net_bands.length <= 0
|
389
|
+
return false
|
390
|
+
end
|
391
|
+
|
392
|
+
if (net_bands & next_bands).empty?
|
393
|
+
true
|
394
|
+
else
|
395
|
+
false
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
protected
|
400
|
+
def next_hop_net_freq_me?
|
401
|
+
unless has_next_hop?
|
402
|
+
return false
|
403
|
+
end
|
404
|
+
|
405
|
+
net_freqs = select_net_freq_hops(@port_info.values.map {|info| info[:port_net].upcase })
|
406
|
+
next_freqs= select_net_freq_hops([select_next_hop(@frame[:path]).upcase])
|
407
|
+
|
408
|
+
if next_freqs.length <= 0 or net_freqs.length <= 0
|
409
|
+
return false
|
410
|
+
end
|
411
|
+
|
412
|
+
next_band = next_bands[0]
|
413
|
+
|
414
|
+
if net_bands.include? next_band
|
415
|
+
false
|
416
|
+
else
|
417
|
+
true
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
protected
|
422
|
+
def future_hop_net_freq_me?
|
423
|
+
unless has_next_hop?
|
424
|
+
return false
|
425
|
+
end
|
426
|
+
|
427
|
+
net_freqs = select_net_freq_hops(@port_info.values.map {|info| info[:port_net].upcase })
|
428
|
+
next_freqs = select_net_freq_hops(select_future_hops(Rules.array_upcase(@frame[:path])))
|
429
|
+
|
430
|
+
if next_freqs.length <= 0 or net_freqs.length <= 0
|
431
|
+
return false
|
432
|
+
end
|
433
|
+
|
434
|
+
if (net_freqs & next_freqs).empty?
|
435
|
+
true
|
436
|
+
else
|
437
|
+
false
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
protected
|
442
|
+
def next_hop_old_paradigm?
|
443
|
+
unless has_next_hop?
|
444
|
+
return false
|
445
|
+
end
|
446
|
+
|
447
|
+
port_name = @frame_port
|
448
|
+
port_info = @port_info[port_name]
|
449
|
+
|
450
|
+
if !port_info.key? :old_paradigm or port_info[:old_paradigm].length <= 0
|
451
|
+
return false
|
452
|
+
end
|
453
|
+
|
454
|
+
old_paradigm = Rules.array_upcase(port_info[:old_paradigm])
|
455
|
+
next_hop = select_next_hop(@frame[:path]).upcase
|
456
|
+
|
457
|
+
Rules.is_hop_old_paradigm?(next_hop, old_paradigm)
|
458
|
+
end
|
459
|
+
|
460
|
+
protected
|
461
|
+
def future_hop_old_paradigm?
|
462
|
+
unless has_next_hop?
|
463
|
+
return false
|
464
|
+
end
|
465
|
+
|
466
|
+
port_name = @frame_port
|
467
|
+
port_info = @port_info[port_name]
|
468
|
+
|
469
|
+
if !port_info.key? :old_paradigm or port_info[:old_paradigm].length <= 0
|
470
|
+
return false
|
471
|
+
end
|
472
|
+
|
473
|
+
old_paradigm = Rules.array_upcase(port_info[:old_paradigm])
|
474
|
+
future_hops = select_future_hops(@frame[:path]).map { |s| s.upcase }
|
475
|
+
|
476
|
+
old_paradigm.each do |target|
|
477
|
+
if target.is_a? Regexp
|
478
|
+
future_hops.each do |hop|
|
479
|
+
if hop =~ target
|
480
|
+
return true
|
481
|
+
end
|
482
|
+
end
|
483
|
+
elsif future_hops.include? target
|
484
|
+
return true
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
false
|
489
|
+
end
|
490
|
+
|
491
|
+
protected
|
492
|
+
def next_hop_new_paradigm?
|
493
|
+
unless has_next_hop?
|
494
|
+
return false
|
495
|
+
end
|
496
|
+
|
497
|
+
port_name = @frame_port
|
498
|
+
port_info = @port_info[port_name]
|
499
|
+
|
500
|
+
if !port_info.key? :new_paradigm or port_info[:new_paradigm].length <= 0
|
501
|
+
return false
|
502
|
+
end
|
503
|
+
|
504
|
+
new_paradigm_all = port_info[:new_paradigm]
|
505
|
+
next_hop = select_next_hop(@frame[:path]).upcase
|
506
|
+
|
507
|
+
Rules.is_hop_new_paradigm?(next_hop, new_paradigm_all)
|
508
|
+
end
|
509
|
+
|
510
|
+
protected
|
511
|
+
def future_hop_new_paradigm?
|
512
|
+
unless has_next_hop?
|
513
|
+
return false
|
514
|
+
end
|
515
|
+
|
516
|
+
port_name = @frame_port
|
517
|
+
port_info = @port_info[port_name]
|
518
|
+
|
519
|
+
if !port_info.key? :new_paradigm or port_info[:new_paradigm].length <= 0
|
520
|
+
return false
|
521
|
+
end
|
522
|
+
|
523
|
+
new_paradigm_all = port_info[:new_paradigm]
|
524
|
+
future_hops = select_future_hops(@frame[:path]).map { |s| s.upcase }
|
525
|
+
|
526
|
+
|
527
|
+
new_paradigm_all.each do |new_paradigm|
|
528
|
+
if new_paradigm[:target].is_a? Regexp
|
529
|
+
future_hops.each do |hop|
|
530
|
+
if hop.include? '-'
|
531
|
+
hop_split = hop.split('-')
|
532
|
+
if hop_split and hop_split.length == 2
|
533
|
+
hop_target = hop_split[0]
|
534
|
+
hop_hops = hop_split[1].to_i
|
535
|
+
if hop_hops > 0 and hop_target =~ new_paradigm[:target]
|
536
|
+
return true
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
elsif future_hops.include? new_paradigm[:target].upcase
|
542
|
+
return true
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
false
|
547
|
+
end
|
548
|
+
|
549
|
+
protected
|
550
|
+
def next_hop_net_me?
|
551
|
+
if next_hop_net_band_me? or next_hop_net_freq_me?
|
552
|
+
true
|
553
|
+
else
|
554
|
+
false
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
protected
|
559
|
+
def future_hop_net_me?
|
560
|
+
if future_hop_net_band_me? or future_hop_net_freq_me?
|
561
|
+
true
|
562
|
+
else
|
563
|
+
false
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
protected
|
568
|
+
def next_hop_paradigm?
|
569
|
+
if next_hop_new_paradigm? or next_hop_old_paradigm?
|
570
|
+
true
|
571
|
+
else
|
572
|
+
false
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
protected
|
577
|
+
def future_hop_paradigm?
|
578
|
+
if future_hop_new_paradigm? or future_hop_old_paradigm?
|
579
|
+
true
|
580
|
+
else
|
581
|
+
false
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
protected
|
586
|
+
def next_hop_me?
|
587
|
+
if next_hop_net_me? or next_hop_identifier_me? or next_hop_paradigm?
|
588
|
+
true
|
589
|
+
else
|
590
|
+
false
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
protected
|
595
|
+
def future_hop_me?
|
596
|
+
if future_hop_net_me? or future_hop_identifier_me? or future_hop_paradigm?
|
597
|
+
true
|
598
|
+
else
|
599
|
+
false
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
protected
|
604
|
+
def mine?
|
605
|
+
if future_hop_me? or destination_me?
|
606
|
+
true
|
607
|
+
else
|
608
|
+
false
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
public
|
613
|
+
attr_reader :next_target, :frame
|
614
|
+
end
|
615
|
+
|
616
|
+
class Route
|
617
|
+
@@chains = {}
|
618
|
+
|
619
|
+
public
|
620
|
+
def self.inbound_chain(&block)
|
621
|
+
@@chains[:inbound] = {:target => :input, :block => block}
|
622
|
+
end
|
623
|
+
|
624
|
+
public
|
625
|
+
def self.outbound_chain(&block)
|
626
|
+
@@chains[:outbound] = {:target => :output, :block => block}
|
627
|
+
end
|
628
|
+
|
629
|
+
public
|
630
|
+
def self.side_chain(name, target, &block)
|
631
|
+
@@chains[name] = {:target => target, :block => block}
|
632
|
+
end
|
633
|
+
|
634
|
+
public
|
635
|
+
def self.reset
|
636
|
+
@@chains.clear
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
# ==== This is the part the user will implement ======
|
641
|
+
|
642
|
+
Route.inbound_chain {
|
643
|
+
filter destination_me?, :pass, :forward
|
644
|
+
filter :input # if this werent here packet would be dropped
|
645
|
+
}
|
646
|
+
|
647
|
+
Route.outbound_chain {
|
648
|
+
filter :output # if this werent here packet would be dropped
|
649
|
+
}
|
650
|
+
|
651
|
+
Route.side_chain(:forward, :output) {
|
652
|
+
filter seen?, :drop
|
653
|
+
filter future_hop_me?, :pass, :drop
|
654
|
+
route :output
|
655
|
+
}
|
656
|
+
|
657
|
+
# ==== Exiting custom code ========
|
658
|
+
|
659
|
+
class Route
|
660
|
+
public
|
661
|
+
def self.handle_frame(frame, config, is_inbound, name)
|
662
|
+
initial_chain = is_inbound ? :inbound : :outbound
|
663
|
+
unless @@chains.key? initial_chain
|
664
|
+
return nil
|
665
|
+
end
|
666
|
+
|
667
|
+
rules = Rules.new(frame, config, name, initial_chain)
|
668
|
+
more = true
|
669
|
+
while more and rules.next_target != :input and rules.next_target != :output and rules.next_target != :drop
|
670
|
+
catch(:new_target) do
|
671
|
+
rules.instance_eval &@@chains[rules.next_target][:block]
|
672
|
+
more = false
|
673
|
+
end
|
674
|
+
end
|
675
|
+
|
676
|
+
if rules.next_target == :drop or !more
|
677
|
+
return nil
|
678
|
+
end
|
679
|
+
|
680
|
+
{
|
681
|
+
:output_target => rules.next_target == :output ? name : nil,
|
682
|
+
:frame => rules.frame
|
683
|
+
}
|
684
|
+
end
|
685
|
+
end
|
686
|
+
end
|
687
|
+
end
|
data/peak.conf.example
CHANGED
@@ -20,12 +20,19 @@ PORT KENWOOD-1:
|
|
20
20
|
identifier: WI2ARD-1
|
21
21
|
net: 2M1
|
22
22
|
tnc_port: 0
|
23
|
-
beacon_path: [WIDE1-1, WIDE2-2]
|
24
|
-
status_path: [WIDE1-1, WIDE2-2]
|
23
|
+
beacon_path: ['WIDE1-1', 'WIDE2-2']
|
24
|
+
status_path: ['WIDE1-1', 'WIDE2-2']
|
25
25
|
beacon_text: '!/:=i@;N.G& --PHG5790/G/D R-I-R H24 C30'
|
26
26
|
status_text: '>Listening on 146.52Mhz http://JeffreyFreeman.me'
|
27
27
|
id_text: 'WI2ARD/30M1 GATE/2M1 WI2ARD-1/2M1 WIDEN-n IGATE'
|
28
|
-
id_path: [WIDE1-1, WIDE2-2]
|
28
|
+
id_path: ['WIDE1-1', 'WIDE2-2']
|
29
|
+
preemptive: true
|
30
|
+
old_paradigm: ['ECHO', 'PA']
|
31
|
+
new_paradigm:
|
32
|
+
- target: !ruby/regexp '/^WIDE[1-2]{1}$/i'
|
33
|
+
max_hops: 2
|
34
|
+
- target: !ruby/regexp '/^PA[1-5]{1}$/i'
|
35
|
+
max_hops: 5
|
29
36
|
|
30
37
|
PORT RPR-1:
|
31
38
|
identifier: WI2ARD
|
@@ -37,6 +44,13 @@ PORT RPR-1:
|
|
37
44
|
status_text: '>Robust Packet Radio http://JeffreyFreeman.me'
|
38
45
|
id_text: 'WI2ARD/30M1 GATE/2M1 WI2ARD-1/2M1 WIDEN-n IGATE'
|
39
46
|
id_path: [WIDE1-1]
|
47
|
+
preemptive: true
|
48
|
+
old_paradigm: ['ECHO', 'PA']
|
49
|
+
new_paradigm:
|
50
|
+
- target: !ruby/regexp '/^WIDE[1-2]{1}$/i'
|
51
|
+
max_hops: 2
|
52
|
+
- target: !ruby/regexp '/^PA[1-5]{1}$/i'
|
53
|
+
max_hops: 5
|
40
54
|
|
41
55
|
IGATE:
|
42
56
|
callsign: WI2ARD
|
data/peak.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.email = ['jeffrey.freeman@syncleus.com']
|
12
12
|
|
13
13
|
spec.summary = %q{Reference implementation for the APEX Radio protocol, an extension to APRS.}
|
14
|
-
spec.description = %q{Reference implementation for the APEX Radio protocol, which is
|
14
|
+
spec.description = %q{Reference implementation for the APEX Radio protocol, which is itself an extension to the APRS protocol.}
|
15
15
|
spec.homepage = 'http://apexprotocol.com'
|
16
16
|
|
17
17
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: peak
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeffrey Phillips Freeman
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-10-
|
11
|
+
date: 2016-10-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -108,7 +108,7 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0.14'
|
111
|
-
description: Reference implementation for the APEX Radio protocol, which is
|
111
|
+
description: Reference implementation for the APEX Radio protocol, which is itself
|
112
112
|
an extension to the APRS protocol.
|
113
113
|
email:
|
114
114
|
- jeffrey.freeman@syncleus.com
|
@@ -118,6 +118,7 @@ extra_rdoc_files: []
|
|
118
118
|
files:
|
119
119
|
- ".gitignore"
|
120
120
|
- CHANGELOG.md
|
121
|
+
- CONTRIBUTORS.md
|
121
122
|
- Gemfile
|
122
123
|
- LICENSE
|
123
124
|
- README.rdoc
|
@@ -135,6 +136,7 @@ files:
|
|
135
136
|
- lib/peak/plugins/id.rb
|
136
137
|
- lib/peak/plugins/plugin_factory.rb
|
137
138
|
- lib/peak/plugins/status.rb
|
139
|
+
- lib/peak/routing/route.rb
|
138
140
|
- lib/peak/tnc_port.rb
|
139
141
|
- peak.conf.example
|
140
142
|
- peak.gemspec
|