naoki86star

インターネットの片隅でなにかしら書いてみる

ordering flowspec by python via gobgpd

 gobgpのgrpcとかbgpのflowspecとか見てきた流れで、ならばgobgpdつかっているときにcliスクリプトからflowspec投げるには?ということで、とりあえずできるようになる要点をピックアップ。デーモンconfigurationではafi-safi-name = "ipv4-flowspec", afi-safi-name = "ipv6-flowspec"を設定。

必須import

from google.protobuf.any_pb2 import Any
import gobgp_pb2
import gobgp_pb2_grpc
import attribute_pb2

デーモンとの通信チャネル作成

def connfactory(host, port):
    channel = grpc.insecure_channel(host + ':' + str(50051))
    stub = gobgp_pb2_grpc.GobgpApiStub(channel)
    return stub

最後の実行で、デーモンサイトのlocal-asnが必要

def getbgp(stub):
    g = stub.GetBgp(gobgp_pb2.GetBgpRequest())
    ret = {}
    for c in g.ListFields()[0]:
        if c.__class__.__name__ != "Global":
            continue
        for f in c.ListFields():
            if f[0].__class__.__name__ == "FieldDescriptor":
                ret[f[0].name] = f[1]
    return ret

IPv4 or Ipv6 on flowspec おまじない変数

def family(v):
    afi = (v==4 and gobgp_pb2.Family.AFI_IP  or  gobgp_pb2.Family.Family.AFI_IP6)
    return gobgp_pb2.Family(afi=afi, safi=gobgp_pb2.Family.SAFI_FLOW_SPEC_UNICAST)

Network Layer Reachability Information部分の作成

  • RFC的には”設定する順番には責任もってね"とある。typeを守るのは容易。複数の値条件の部分が?
  • OptionのE bit(続くか終わりか)はライブラリがつけてくれている気がする。
  • operatorで試してあるのは==だけ。。ANDも試せてない。
  • type 7以降もやってないけども、特にtype11 DSCPというのが?
def build_flowspecipprefix(rules, typeval, ipprefix):
    assert(typeval in (1, 2))
    s = ipprefix.split('/')
    prefix = s[0]
    prefix_len = (len(s)==2 and int(s[1])) or (prefix.find(':')>-1 and 128 or 32) # be more seriously
    o = Any()
    o.Pack(attribute_pb2.FlowSpecIPPrefix(type=typeval,prefix_len = prefix_len, prefix = prefix))
    rules.append(o)

def build_flowspeccomponent(rules, typeval, conditions):
    assert(typeval in (3, 4, 5, 6,))
    items = []
    for op, value in conditions:
        items.append(attribute_pb2.FlowSpecComponentItem(op=op, value=value))
    o = Any()
    o.Pack(attribute_pb2.FlowSpecComponent(type=typeval, items=items))
    rules.append(o)

flowspec action

  • 今回の例ではtraffic-rateだけ*1
  • TrafficRateExtendedには引数に'as’が定義されているが、文法上の理由で、pythonスクリプトから設定できない。この領域にはASNの下位2byteをいれておけとあるが、デフォルト値0(だとおもわれる)で問題ないのだと思う。*2
def build_action(rate):
    rate = float(rate)
    o = Any()
    o.Pack(attribute_pb2.TrafficRateExtended(rate=rate)) # can't let into 'as'
    return attribute_pb2.ExtendedCommunitiesAttribute(communities=[o])

AddPathで必要な引数コンテナの作成ヘルパ

def build_pattrs(v, nlri, rate, origin):
  ac = Any()
  ac.Pack(build_action(rate))

  nullip = (v==4 and "0.0.0.0" or "::0")
  nh = Any()
  nh.Pack(attribute_pb2.MpReachNLRIAttribute(family=family(v), nlris=[nlri], next_hops=[nullip]))

  og = Any()
  og.Pack(attribute_pb2.OriginAttribute(origin=origin))
  return (ac, nh, og)

def build_nlri(rules):
    o = Any()
    o.Pack(attribute_pb2.FlowSpecNLRI(rules=rules))
    return o

材料がそろったところで実行例。
src: 1.2.3.4/32
dst:9.8.7.6/32
protocol: udp
port(両方向): 1900,11211
rate-limit 0.0 bytes/sec :=> discard
の条件について

ver = 4
rules = []
build_flowspecipprefix(rules, 1, "1.2.3.4/32")
build_flowspecipprefix(rules, 2, "9.8.7.6/32")
build_flowspeccomponent(rules, 3, [(1,17)])
build_flowspeccomponent(rules, 4, [(1,1900),(1,11211),])
nlri = build_nlri(rules)

stub = connfactory('127.0.0.1', 50051)
source_asn = getbgp(stub)['as']

path = gobgp_pb2.Path(family=family(ver), nlri=nlri, pattrs=build_pattrs(ver, nlri, 0.0, 0), source_asn=source_asn)

stub.AddPath(gobgp_pb2.AddPathRequest(table_type=gobgp_pb2.GLOBAL,path=path),timeout=10)

情報が入ったかどうかをgobgpコマンドで確認

nao@hnd:~/gobgp$ ./gobgp global rib -a ipv4-flowspec
   Network                                                                              Next Hop             AS_PATH              Age        Attrs
*> [destination: 1.2.3.4/32][source: 9.8.7.6/32][protocol: ==udp][port: ==1900 ==11211] fictitious                                00:00:27   [{Extcomms: [discard]} {Origin: i}]

以上です。
参考:

*1:多分traffic-actionとかそれぞれのタイプ用にpythonクラスあるだろうと推測

*2:vMXで試したときには0で動いていた