Line | Count | Source |
1 | | // SPDX-License-Identifier: ISC |
2 | | /* |
3 | | * FRR switchable defaults. |
4 | | * Copyright (c) 2017-2019 David Lamparter, for NetDEF, Inc. |
5 | | */ |
6 | | |
7 | | #include <zebra.h> |
8 | | |
9 | | #include "defaults.h" |
10 | | #include "lib/version.h" |
11 | | |
12 | | static char df_version[128] = FRR_VER_SHORT, df_profile[128] = DFLT_NAME; |
13 | | static struct frr_default *dflt_first = NULL, **dflt_next = &dflt_first; |
14 | | |
15 | | /* these are global for all FRR daemons. they have to be, since we write an |
16 | | * integrated config with the same value for all daemons. |
17 | | */ |
18 | | const char *frr_defaults_profiles[] = { |
19 | | "traditional", |
20 | | "datacenter", |
21 | | NULL, |
22 | | }; |
23 | | |
24 | | static int version_value(int ch) |
25 | 0 | { |
26 | | /* non-ASCII shouldn't happen */ |
27 | 0 | if (ch < 0 || ch >= 128) |
28 | 0 | return 2; |
29 | | |
30 | | /* ~foo sorts older than nothing */ |
31 | 0 | if (ch == '~') |
32 | 0 | return 0; |
33 | 0 | if (ch == '\0') |
34 | 0 | return 1; |
35 | 0 | if (isalpha(ch)) |
36 | 0 | return 0x100 + tolower(ch); |
37 | | |
38 | | /* punctuation and digits (and everything else) */ |
39 | 0 | return 0x200 + ch; |
40 | 0 | } |
41 | | |
42 | | int frr_version_cmp(const char *aa, const char *bb) |
43 | 20 | { |
44 | 20 | const char *apos = aa, *bpos = bb; |
45 | | |
46 | | /* || is correct, we won't scan past the end of a string since that |
47 | | * doesn't compare equal to anything else */ |
48 | 20 | while (apos[0] || bpos[0]) { |
49 | 20 | if (isdigit((unsigned char)apos[0]) && |
50 | 20 | isdigit((unsigned char)bpos[0])) { |
51 | 20 | unsigned long av, bv; |
52 | 20 | char *aend = NULL, *bend = NULL; |
53 | | |
54 | 20 | av = strtoul(apos, &aend, 10); |
55 | 20 | bv = strtoul(bpos, &bend, 10); |
56 | 20 | if (av < bv) |
57 | 0 | return -1; |
58 | 20 | if (av > bv) |
59 | 20 | return 1; |
60 | | |
61 | 0 | apos = aend; |
62 | 0 | bpos = bend; |
63 | 0 | continue; |
64 | 20 | } |
65 | | |
66 | 0 | int a = version_value(*apos++); |
67 | 0 | int b = version_value(*bpos++); |
68 | |
|
69 | 0 | if (a < b) |
70 | 0 | return -1; |
71 | 0 | if (a > b) |
72 | 0 | return 1; |
73 | 0 | } |
74 | 0 | return 0; |
75 | 20 | } |
76 | | |
77 | | static void frr_default_apply_one(struct frr_default *dflt, bool check); |
78 | | |
79 | | void frr_default_add(struct frr_default *dflt) |
80 | 26 | { |
81 | 26 | dflt->next = NULL; |
82 | 26 | *dflt_next = dflt; |
83 | 26 | dflt_next = &dflt->next; |
84 | | |
85 | 26 | frr_default_apply_one(dflt, true); |
86 | 26 | } |
87 | | |
88 | | static bool frr_match_version(const char *name, const char *vspec, |
89 | | const char *version, bool check) |
90 | 20 | { |
91 | 20 | int cmp; |
92 | 20 | static const struct spec { |
93 | 20 | const char *str; |
94 | 20 | int dir, eq; |
95 | 20 | } specs[] = { |
96 | 20 | {"<=", -1, 1}, |
97 | 20 | {">=", 1, 1}, |
98 | 20 | {"==", 0, 1}, |
99 | 20 | {"<", -1, 0}, |
100 | 20 | {">", 1, 0}, |
101 | 20 | {"=", 0, 1}, |
102 | 20 | {NULL, 0, 0}, |
103 | 20 | }; |
104 | 20 | const struct spec *s; |
105 | | |
106 | 20 | if (!vspec) |
107 | | /* NULL = all versions */ |
108 | 0 | return true; |
109 | | |
110 | 80 | for (s = specs; s->str; s++) |
111 | 80 | if (!strncmp(s->str, vspec, strlen(s->str))) |
112 | 20 | break; |
113 | 20 | if (!s->str) { |
114 | 0 | if (check) |
115 | 0 | fprintf(stderr, "invalid version specifier for %s: %s", |
116 | 0 | name, vspec); |
117 | | /* invalid version spec, never matches */ |
118 | 0 | return false; |
119 | 0 | } |
120 | | |
121 | 20 | vspec += strlen(s->str); |
122 | 20 | while (isspace((unsigned char)*vspec)) |
123 | 20 | vspec++; |
124 | | |
125 | 20 | cmp = frr_version_cmp(version, vspec); |
126 | 20 | if (cmp == s->dir || (s->eq && cmp == 0)) |
127 | 0 | return true; |
128 | | |
129 | 20 | return false; |
130 | 20 | } |
131 | | |
132 | | static void frr_default_apply_one(struct frr_default *dflt, bool check) |
133 | 26 | { |
134 | 26 | struct frr_default_entry *entry = dflt->entries; |
135 | 26 | struct frr_default_entry *dfltentry = NULL, *saveentry = NULL; |
136 | | |
137 | 54 | for (; entry->match_version || entry->match_profile; entry++) { |
138 | 28 | if (entry->match_profile |
139 | 20 | && strcmp(entry->match_profile, df_profile)) |
140 | 18 | continue; |
141 | | |
142 | 10 | if (!dfltentry && frr_match_version(dflt->name, |
143 | 10 | entry->match_version, df_version, check)) |
144 | 0 | dfltentry = entry; |
145 | 10 | if (!saveentry && frr_match_version(dflt->name, |
146 | 10 | entry->match_version, FRR_VER_SHORT, check)) |
147 | 0 | saveentry = entry; |
148 | | |
149 | 10 | if (dfltentry && saveentry && !check) |
150 | 0 | break; |
151 | 10 | } |
152 | | /* found default or arrived at last entry that has NULL,NULL spec */ |
153 | | |
154 | 26 | if (!dfltentry) |
155 | 26 | dfltentry = entry; |
156 | 26 | if (!saveentry) |
157 | 26 | saveentry = entry; |
158 | | |
159 | 26 | if (dflt->dflt_bool) |
160 | 20 | *dflt->dflt_bool = dfltentry->val_bool; |
161 | 26 | if (dflt->dflt_str) |
162 | 0 | *dflt->dflt_str = dfltentry->val_str; |
163 | 26 | if (dflt->dflt_long) |
164 | 0 | *dflt->dflt_long = dfltentry->val_long; |
165 | 26 | if (dflt->dflt_ulong) |
166 | 6 | *dflt->dflt_ulong = dfltentry->val_ulong; |
167 | 26 | if (dflt->dflt_float) |
168 | 0 | *dflt->dflt_float = dfltentry->val_float; |
169 | 26 | if (dflt->save_bool) |
170 | 20 | *dflt->save_bool = saveentry->val_bool; |
171 | 26 | if (dflt->save_str) |
172 | 0 | *dflt->save_str = saveentry->val_str; |
173 | 26 | if (dflt->save_long) |
174 | 0 | *dflt->save_long = saveentry->val_long; |
175 | 26 | if (dflt->save_ulong) |
176 | 6 | *dflt->save_ulong = saveentry->val_ulong; |
177 | 26 | if (dflt->save_float) |
178 | 0 | *dflt->save_float = saveentry->val_float; |
179 | 26 | } |
180 | | |
181 | | void frr_defaults_apply(void) |
182 | 0 | { |
183 | 0 | struct frr_default *dflt; |
184 | |
|
185 | 0 | for (dflt = dflt_first; dflt; dflt = dflt->next) |
186 | 0 | frr_default_apply_one(dflt, false); |
187 | 0 | } |
188 | | |
189 | | bool frr_defaults_profile_valid(const char *profile) |
190 | 0 | { |
191 | 0 | const char **p; |
192 | |
|
193 | 0 | for (p = frr_defaults_profiles; *p; p++) |
194 | 0 | if (!strcmp(profile, *p)) |
195 | 0 | return true; |
196 | 0 | return false; |
197 | 0 | } |
198 | | |
199 | | const char *frr_defaults_version(void) |
200 | 0 | { |
201 | 0 | return df_version; |
202 | 0 | } |
203 | | |
204 | | const char *frr_defaults_profile(void) |
205 | 0 | { |
206 | 0 | return df_profile; |
207 | 0 | } |
208 | | |
209 | | void frr_defaults_version_set(const char *version) |
210 | 0 | { |
211 | 0 | strlcpy(df_version, version, sizeof(df_version)); |
212 | 0 | frr_defaults_apply(); |
213 | 0 | } |
214 | | |
215 | | void frr_defaults_profile_set(const char *profile) |
216 | 0 | { |
217 | 0 | strlcpy(df_profile, profile, sizeof(df_profile)); |
218 | 0 | frr_defaults_apply(); |
219 | 0 | } |