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