Add support for unified Bird daemons

Bird 2.0 includes IPv4 and IPv6 support in a single daemon.
When UNIFIED_DAEMON is set to True, queries are always done with the
"ipv4" backend (which handles both protocols), and distinctions between
IPv4 and IPv4 are removed in the UI.
This commit is contained in:
Zhaofeng Li 2018-11-28 10:49:44 -08:00 committed by Simon Marsh
parent daaf981b32
commit ddf86d02fb
5 changed files with 71 additions and 30 deletions

4
lg.cfg
View File

@ -13,6 +13,10 @@ PROXY = {
"h3": "h3.some.network:5000", "h3": "h3.some.network:5000",
} }
# If True, queries are always done with the "ipv4" backend,
# and the distinction between IPv4 and IPv6 is removed from the UI.
UNIFIED_DAEMON = True
# Used for bgpmap # Used for bgpmap
ROUTER_IP = { ROUTER_IP = {
"gw" : [ "91.224.148.2", "2a01:6600:8000::175" ], "gw" : [ "91.224.148.2", "2a01:6600:8000::175" ],

80
lg.py
View File

@ -32,7 +32,7 @@ from urllib import quote, unquote
import json import json
import random import random
from toolbox import mask_is_valid, ipv6_is_valid, ipv4_is_valid, resolve, save_cache_pickle, load_cache_pickle, unescape from toolbox import mask_is_valid, ip_is_valid, ipv6_is_valid, ipv4_is_valid, resolve, resolve_any, save_cache_pickle, load_cache_pickle, unescape
#from xml.sax.saxutils import escape #from xml.sax.saxutils import escape
@ -119,7 +119,10 @@ def whois_command(query):
def bird_command(host, proto, query): def bird_command(host, proto, query):
"""Alias to bird_proxy for bird service""" """Alias to bird_proxy for bird service"""
return bird_proxy(host, proto, "bird", query) if app.config.get("UNIFIED_DAEMON", False):
return bird_proxy(host, "ipv4", "bird", query)
else:
return bird_proxy(host, proto, "bird", query)
def bird_proxy(host, proto, service, query): def bird_proxy(host, proto, service, query):
@ -305,16 +308,23 @@ def traceroute(hosts, proto):
set_session("traceroute", hosts, proto, q) set_session("traceroute", hosts, proto, q)
if proto == "ipv6" and not ipv6_is_valid(q): if app.config.get("UNIFIED_DAEMON", False):
try: if not ip_is_valid(q):
q = resolve(q, "AAAA") try:
except: q = resolve_any(q)
return error_page("%s is unresolvable or invalid for %s" % (q, proto)) except:
if proto == "ipv4" and not ipv4_is_valid(q): return error_page("%s is unresolvable" % q)
try: else:
q = resolve(q, "A") if proto == "ipv6" and not ipv6_is_valid(q):
except: try:
return error_page("%s is unresolvable or invalid for %s" % (q, proto)) q = resolve(q, "AAAA")
except:
return error_page("%s is unresolvable or invalid for %s" % (q, proto))
if proto == "ipv4" and not ipv4_is_valid(q):
try:
q = resolve(q, "A")
except:
return error_page("%s is unresolvable or invalid for %s" % (q, proto))
errors = [] errors = []
infos = {} infos = {}
@ -608,23 +618,37 @@ def show_route(request_type, hosts, proto):
if len(expression.split("/")) == 2: if len(expression.split("/")) == 2:
expression, mask = (expression.split("/")) expression, mask = (expression.split("/"))
if not mask and proto == "ipv4": if app.config.get("UNIFIED_DAEMON", False):
mask = "32" if not ip_is_valid(expression):
if not mask and proto == "ipv6": try:
mask = "128" expression = resolve_any(expression)
if not mask_is_valid(mask): except:
return error_page("mask %s is invalid" % mask) return error_page("%s is unresolvable" % expression)
if proto == "ipv6" and not ipv6_is_valid(expression): if not mask and ipv4_is_valid(expression):
try: mask = "32"
expression = resolve(expression, "AAAA") if not mask and ipv6_is_valid(expression):
except: mask = "128"
return error_page("%s is unresolvable or invalid for %s" % (expression, proto)) if not mask_is_valid(mask):
if proto == "ipv4" and not ipv4_is_valid(expression): return error_page("mask %s is invalid" % mask)
try: else:
expression = resolve(expression, "A") if not mask and proto == "ipv4":
except: mask = "32"
return error_page("%s is unresolvable or invalid for %s" % (expression, proto)) if not mask and proto == "ipv6":
mask = "128"
if not mask_is_valid(mask):
return error_page("mask %s is invalid" % mask)
if proto == "ipv6" and not ipv6_is_valid(expression):
try:
expression = resolve(expression, "AAAA")
except:
return error_page("%s is unresolvable or invalid for %s" % (expression, proto))
if proto == "ipv4" and not ipv4_is_valid(expression):
try:
expression = resolve(expression, "A")
except:
return error_page("%s is unresolvable or invalid for %s" % (expression, proto))
if mask: if mask:
expression += "/" + mask expression += "/" + mask

View File

@ -1,7 +1,7 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% block body %} {% block body %}
{% for host in detail %} {% for host in detail %}
<h3>{{host}}/{{session.proto}}: {{command}}</h3> <h3>{{host}}{% if not config.UNIFIED_DAEMON %}/{{session.proto}}{% endif %}: {{command}}</h3>
<i>{{ detail[host].status }}</i><br /><br /> <i>{{ detail[host].status }}</i><br /><br />
<pre> <pre>
{{ detail[host].description|trim|safe }} {{ detail[host].description|trim|safe }}

View File

@ -24,10 +24,12 @@
{% endfor %} {% endfor %}
</ul> </ul>
{% if not config.UNIFIED_DAEMON %}
<div class="btn-group"> <div class="btn-group">
<a class="btn btn-primary proto" id="ipv4" href="#">ipv4</a> <a class="btn btn-primary proto" id="ipv4" href="#">ipv4</a>
<a class="btn btn-primary proto" id="ipv6" href="#">ipv6</a> <a class="btn btn-primary proto" id="ipv6" href="#">ipv6</a>
</div> </div>
{% endif %}
<ul class="nav"> <ul class="nav">
<li class="nav-item dropdown request_type"> <li class="nav-item dropdown request_type">
@ -74,7 +76,7 @@
{% for hosts, proto, request_type, request_args in session.history %} {% for hosts, proto, request_type, request_args in session.history %}
<a class="list-group-item list-group-item-action {% if loop.first %}active{% endif %}" <a class="list-group-item list-group-item-action {% if loop.first %}active{% endif %}"
href="/{{ [request_type, hosts, proto]|join("/") }}{% if request_args %}?q={{request_args}}{% endif %}"> href="/{{ [request_type, hosts, proto]|join("/") }}{% if request_args %}?q={{request_args}}{% endif %}">
{{hosts}}/{{proto}}: {{ commands_dict[request_type]|replace("...", request_args) }} {{hosts}}{% if not config.UNIFIED_DAEMON %}/{{proto}}{% endif %}: {{ commands_dict[request_type]|replace("...", request_args) }}
</a> </a>
{% endfor %} {% endfor %}

View File

@ -31,6 +31,14 @@ resolv.lifetime = 1
def resolve(n, q): def resolve(n, q):
return str(resolv.query(n,q)[0]) return str(resolv.query(n,q)[0])
def resolve_any(h):
try:
return resolve(h, "AAAA")
except:
pass
return resolve(h, "A")
def mask_is_valid(n): def mask_is_valid(n):
if not n: if not n:
return True return True
@ -40,6 +48,9 @@ def mask_is_valid(n):
except: except:
return False return False
def ip_is_valid(n):
return ipv4_is_valid(n) or ipv6_is_valid(n)
def ipv4_is_valid(n): def ipv4_is_valid(n):
try: try:
socket.inet_pton(socket.AF_INET, n) socket.inet_pton(socket.AF_INET, n)