blob: 68a1f51bde81463dd5eb0aabf06aa3c160b43817 [file] [log] [blame]
Sergiusz Bazanski6eaaaf92019-08-02 01:25:31 +02001package provider
2
3import (
4 "strings"
5
6 pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto"
7)
8
9// A raw, unparsed RPSL name/value pair.
10type rpslRawAttribute struct {
11 name string
12 value string
13}
14
15// parseAttributes converts raw RPSL attributes into parsed, structured protos.
16func parseAttributes(attrs []rpslRawAttribute) []*pb.IRRAttribute {
17 res := []*pb.IRRAttribute{}
18
19 for _, attr := range attrs {
20 switch attr.name {
21 case "remarks":
22 res = append(res, &pb.IRRAttribute{
23 Value: &pb.IRRAttribute_Remarks{attr.value},
24 })
25 case "import":
26 ie := parseImportExport(attr.value)
27 if ie != nil {
28 res = append(res, &pb.IRRAttribute{
29 Value: &pb.IRRAttribute_Import{ie},
30 })
31 }
32 case "export":
33 ie := parseImportExport(attr.value)
34 if ie != nil {
35 res = append(res, &pb.IRRAttribute{
36 Value: &pb.IRRAttribute_Export{ie},
37 })
38 }
39 }
40 }
41
42 return res
43}
44
45// parseImportExport tries to parse the RPSL subset for import/export
46// attributes.
47// It's a hand-written single-pass parser which makes it not very good.
48// See rpsl_test.go for examples.
49func parseImportExport(expression string) *pb.IRRAttribute_ImportExport {
50 popToken := func(s string) (string, string) {
51 s = strings.TrimSpace(s)
52 fields := strings.Fields(s)
53 if len(fields) < 2 {
54 return "", s
55 }
56 return fields[0], strings.TrimSpace(s[len(fields[0]):])
57 }
58
59 res := &pb.IRRAttribute_ImportExport{}
60
61 expr := expression
62 var t string
63 t, expr = popToken(expr)
64
65 for {
66 switch t {
67 case "":
68 break
69 case "protocol":
70 t, expr = popToken(expr)
71 res.ProtocolFrom = t
72 t, expr = popToken(expr)
73 continue
74 case "into":
75 t, expr = popToken(expr)
76 res.ProtocolInto = t
77 t, expr = popToken(expr)
78 continue
79 case "from":
80 fallthrough
81 case "to":
82 t, expr = popToken(expr)
83 peering := t
84 actions := []string{}
85 router_us := ""
86 router_them := ""
87
88 t2, expr2 := popToken(expr)
89 for {
90 switch t2 {
91 case "at":
92 t2, expr2 = popToken(expr2)
93 router_us = t2
94 t2, expr2 = popToken(expr2)
95 continue
96 case "action":
97 parts := strings.SplitN(expr2, ";", 2)
98 if len(parts) != 2 {
99 // malformed action, no ';' found - bail
100 return nil
101 }
102 actions = append(actions, parts[0])
103 expr2 = strings.TrimSpace(parts[1])
104 t2, expr2 = popToken(expr2)
105 continue
106 case "":
107 fallthrough
108 case "accept":
109 fallthrough
110 case "announce":
111 t = t2
112 expr = expr2
113 break
114 default:
115 if router_them == "" {
116 router_them = t2
117 t2, expr2 = popToken(expr2)
118 continue
119 }
120 t = t2
121 expr = expr2
122 break
123 }
124 break
125 }
126 res.Expressions = append(res.Expressions, &pb.IRRAttribute_ImportExport_Expression{
127 Peering: peering,
128 RouterUs: router_us,
129 RouterThem: router_them,
130 Actions: actions,
131 })
132 continue
133 case "accept":
134 fallthrough
135 case "announce":
136 res.Filter = expr
137 t = ""
138 continue
139 default:
140 return nil
141 }
142 break
143 }
144
145 return res
146}