manager.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  1. package firewall
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "log"
  7. "os"
  8. "os/exec"
  9. "strings"
  10. "sync"
  11. "time"
  12. )
  13. const (
  14. FilterStorePath = "/etc/proxmox-router/firewall/filter.json"
  15. NATStorePath = "/etc/proxmox-router/firewall/nat.json"
  16. MangleStorePath = "/etc/proxmox-router/firewall/mangle.json"
  17. AddressListsStorePath = "/etc/proxmox-router/firewall/address_lists.json"
  18. )
  19. type FirewallManager struct {
  20. mu sync.Mutex
  21. committed []byte
  22. lastError string
  23. applied bool
  24. filterRules []FilterRule
  25. natRules []NATRule
  26. mangleRules []MangleRule
  27. addressLists []AddressListEntry
  28. geoIPMgr *GeoIPManager
  29. }
  30. func NewFirewallManager() *FirewallManager {
  31. m := &FirewallManager{
  32. filterRules: make([]FilterRule, 0),
  33. natRules: make([]NATRule, 0),
  34. mangleRules: make([]MangleRule, 0),
  35. addressLists: make([]AddressListEntry, 0),
  36. geoIPMgr: NewGeoIPManager(),
  37. }
  38. m.geoIPMgr.referenced = m.geoReferenced
  39. loadErr := m.load()
  40. m.remember()
  41. if loadErr != nil {
  42. m.lastError = loadErr.Error()
  43. log.Printf("Firewall configuration not loaded: %v", loadErr)
  44. return m
  45. }
  46. // Defaults are installed only once, never because a user intentionally saved
  47. // an empty list or deleted all rules.
  48. fresh := true
  49. for _, path := range []string{FilterStorePath, NATStorePath, MangleStorePath, AddressListsStorePath} {
  50. if _, err := os.Stat(path); !os.IsNotExist(err) {
  51. fresh = false
  52. }
  53. }
  54. if fresh {
  55. if err := m.initDefaultRules(); err != nil {
  56. m.lastError = err.Error()
  57. return m
  58. }
  59. }
  60. if err := m.Apply(); err != nil {
  61. log.Printf("Firewall not applied: %v", err)
  62. }
  63. m.remember()
  64. return m
  65. }
  66. func (m *FirewallManager) initDefaultRules() error {
  67. if len(m.natRules) == 0 {
  68. // Default Masquerade NAT only on WAN
  69. m.natRules = append(m.natRules, NATRule{
  70. ID: fmt.Sprintf("nat-%d", time.Now().UnixNano()),
  71. Order: 1,
  72. Chain: "srcnat",
  73. Action: "masquerade",
  74. OutInterfaceList: "WAN",
  75. Comment: "defconf: masquerade outgoing traffic to WAN",
  76. Disabled: false,
  77. })
  78. }
  79. if len(m.filterRules) == 0 {
  80. // Default Fasttrack / Established,Related Filter
  81. m.filterRules = append(m.filterRules, FilterRule{
  82. ID: fmt.Sprintf("filter-%d-1", time.Now().UnixNano()),
  83. Order: 1,
  84. Chain: "input",
  85. Action: "accept",
  86. ConnectionState: []string{"established", "related"},
  87. Comment: "defconf: accept established,related",
  88. Disabled: false,
  89. })
  90. m.filterRules = append(m.filterRules, FilterRule{
  91. ID: fmt.Sprintf("filter-%d-2", time.Now().UnixNano()),
  92. Order: 2,
  93. Chain: "forward",
  94. Action: "accept",
  95. ConnectionState: []string{"established", "related"},
  96. Comment: "defconf: accept established,related forward",
  97. Disabled: false,
  98. })
  99. }
  100. return m.save()
  101. }
  102. func (m *FirewallManager) load() error {
  103. var failures []error
  104. for path, target := range map[string]any{FilterStorePath: &m.filterRules, NATStorePath: &m.natRules, MangleStorePath: &m.mangleRules, AddressListsStorePath: &m.addressLists} {
  105. data, err := os.ReadFile(path)
  106. if os.IsNotExist(err) {
  107. continue
  108. }
  109. if err == nil {
  110. err = json.Unmarshal(data, target)
  111. }
  112. if err != nil {
  113. failures = append(failures, fmt.Errorf("%s: %w", path, err))
  114. }
  115. }
  116. if m.filterRules == nil {
  117. m.filterRules = []FilterRule{}
  118. }
  119. // Older releases implicitly compiled port matches with protocol=all as TCP.
  120. // Keep the existing kernel behavior and expose the actual protocol on edit.
  121. for i := range m.natRules {
  122. r := &m.natRules[i]
  123. if (r.Protocol == "" || r.Protocol == "all") && (r.SrcPort != "" || r.DstPort != "" || r.AnyPort != "") {
  124. r.Protocol = "tcp"
  125. log.Printf("NAT rule %s: migrated legacy implicit TCP port match", r.ID)
  126. }
  127. }
  128. if m.natRules == nil {
  129. m.natRules = []NATRule{}
  130. }
  131. if m.mangleRules == nil {
  132. m.mangleRules = []MangleRule{}
  133. }
  134. if m.addressLists == nil {
  135. m.addressLists = []AddressListEntry{}
  136. }
  137. return errors.Join(failures...)
  138. }
  139. func (m *FirewallManager) snapshot() []byte {
  140. data, _ := json.Marshal([]any{m.filterRules, m.natRules, m.mangleRules, m.addressLists})
  141. return data
  142. }
  143. func (m *FirewallManager) remember() { m.committed = m.snapshot() }
  144. func (m *FirewallManager) restore(data []byte) {
  145. var fields []json.RawMessage
  146. if json.Unmarshal(data, &fields) == nil && len(fields) == 4 {
  147. _ = json.Unmarshal(fields[0], &m.filterRules)
  148. _ = json.Unmarshal(fields[1], &m.natRules)
  149. _ = json.Unmarshal(fields[2], &m.mangleRules)
  150. _ = json.Unmarshal(fields[3], &m.addressLists)
  151. }
  152. }
  153. func (m *FirewallManager) save() error {
  154. if err := os.MkdirAll("/etc/proxmox-router/firewall", 0755); err != nil {
  155. return err
  156. }
  157. files := map[string][]byte{}
  158. for path, value := range map[string]any{FilterStorePath: m.filterRules, NATStorePath: m.natRules, MangleStorePath: m.mangleRules, AddressListsStorePath: m.addressLists} {
  159. data, err := json.MarshalIndent(value, "", " ")
  160. if err != nil {
  161. return err
  162. }
  163. files[path] = data
  164. }
  165. return saveFileGroup(files)
  166. }
  167. func (m *FirewallManager) commitChange() error {
  168. before := append([]byte(nil), m.committed...)
  169. err := m.Apply()
  170. if err == nil {
  171. err = m.save()
  172. }
  173. if err != nil {
  174. m.restore(before)
  175. rollbackErr := m.Apply()
  176. saveErr := m.save()
  177. if rollbackErr != nil || saveErr != nil {
  178. m.lastError = fmt.Sprintf("firewall change failed: %v; rollback apply: %v; rollback save: %v", err, rollbackErr, saveErr)
  179. m.applied = false
  180. return errors.New(m.lastError)
  181. }
  182. return err
  183. }
  184. m.remember()
  185. return nil
  186. }
  187. func (m *FirewallManager) Reapply() error { m.mu.Lock(); defer m.mu.Unlock(); return m.Apply() }
  188. // Apply re-synchronizes AddressLists & GeoIP sets with ipset and applies iptables
  189. func (m *FirewallManager) Apply() (result error) {
  190. defer func() {
  191. m.applied = result == nil
  192. m.lastError = ""
  193. if result != nil {
  194. m.lastError = result.Error()
  195. }
  196. }()
  197. for _, r := range m.filterRules {
  198. if err := validateRule("filter", r); err != nil {
  199. return err
  200. }
  201. }
  202. for _, r := range m.natRules {
  203. if err := validateRule("nat", r); err != nil {
  204. return err
  205. }
  206. }
  207. for _, r := range m.mangleRules {
  208. if err := validateRule("mangle", r); err != nil {
  209. return err
  210. }
  211. }
  212. // Sync GeoIP country sets
  213. if m.geoIPMgr != nil {
  214. // Also ensure any GeoIP lists explicitly referenced in active rules are loaded into ipset
  215. for _, r := range m.filterRules {
  216. if !r.Disabled {
  217. if r.SrcGeoIP != "" {
  218. if err := m.geoIPMgr.EnsureCountryOrPreset(r.SrcGeoIP); err != nil {
  219. return err
  220. }
  221. }
  222. if r.DstGeoIP != "" {
  223. if err := m.geoIPMgr.EnsureCountryOrPreset(r.DstGeoIP); err != nil {
  224. return err
  225. }
  226. }
  227. }
  228. }
  229. for _, r := range m.natRules {
  230. if !r.Disabled {
  231. if r.SrcGeoIP != "" {
  232. if err := m.geoIPMgr.EnsureCountryOrPreset(r.SrcGeoIP); err != nil {
  233. return err
  234. }
  235. }
  236. if r.DstGeoIP != "" {
  237. if err := m.geoIPMgr.EnsureCountryOrPreset(r.DstGeoIP); err != nil {
  238. return err
  239. }
  240. }
  241. }
  242. }
  243. for _, r := range m.mangleRules {
  244. if !r.Disabled {
  245. if r.SrcGeoIP != "" {
  246. if err := m.geoIPMgr.EnsureCountryOrPreset(r.SrcGeoIP); err != nil {
  247. return err
  248. }
  249. }
  250. if r.DstGeoIP != "" {
  251. if err := m.geoIPMgr.EnsureCountryOrPreset(r.DstGeoIP); err != nil {
  252. return err
  253. }
  254. }
  255. }
  256. }
  257. }
  258. // Collect any custom address lists referenced in active rules
  259. var refLists []string
  260. for _, r := range m.filterRules {
  261. if !r.Disabled {
  262. if r.SrcAddressList != "" {
  263. refLists = append(refLists, r.SrcAddressList)
  264. }
  265. if r.DstAddressList != "" {
  266. refLists = append(refLists, r.DstAddressList)
  267. }
  268. if r.AddressList != "" {
  269. refLists = append(refLists, r.AddressList)
  270. }
  271. }
  272. }
  273. for _, r := range m.natRules {
  274. if !r.Disabled {
  275. if r.SrcAddressList != "" {
  276. refLists = append(refLists, r.SrcAddressList)
  277. }
  278. if r.DstAddressList != "" {
  279. refLists = append(refLists, r.DstAddressList)
  280. }
  281. if r.AddressList != "" {
  282. refLists = append(refLists, r.AddressList)
  283. }
  284. }
  285. }
  286. for _, r := range m.mangleRules {
  287. if !r.Disabled {
  288. if r.SrcAddressList != "" {
  289. refLists = append(refLists, r.SrcAddressList)
  290. }
  291. if r.DstAddressList != "" {
  292. refLists = append(refLists, r.DstAddressList)
  293. }
  294. if r.AddressList != "" {
  295. refLists = append(refLists, r.AddressList)
  296. }
  297. }
  298. }
  299. // Sync custom ipset address lists
  300. if err := SyncAddressLists(m.addressLists, refLists...); err != nil {
  301. return err
  302. }
  303. if err := ApplyDualStack(m.filterRules, m.natRules, m.mangleRules); err != nil {
  304. return err
  305. }
  306. collectUnusedAddressLists(m.addressLists, refLists)
  307. return nil
  308. }
  309. func (m *FirewallManager) GeoIP() *GeoIPManager {
  310. return m.geoIPMgr
  311. }
  312. // ==================== Filter Rules ====================
  313. func (m *FirewallManager) ListFilterRules() []FilterRule {
  314. m.mu.Lock()
  315. defer m.mu.Unlock()
  316. counters, _ := ReadRuleCounters(m.filterRules, m.natRules, m.mangleRules)
  317. res := make([]FilterRule, len(m.filterRules))
  318. for i, r := range m.filterRules {
  319. res[i] = r
  320. if c, ok := counters[r.ID]; ok {
  321. res[i].Packets = c[0]
  322. res[i].Bytes = c[1]
  323. }
  324. }
  325. return res
  326. }
  327. func (m *FirewallManager) SaveFilterRule(r FilterRule) error {
  328. m.mu.Lock()
  329. defer m.mu.Unlock()
  330. if err := validateChainPlacement("filter", r); err != nil {
  331. return err
  332. }
  333. if r.ID == "" {
  334. r.ID = fmt.Sprintf("filter-%d", time.Now().UnixNano())
  335. }
  336. found := false
  337. for i, existing := range m.filterRules {
  338. if existing.ID == r.ID {
  339. r.Order = existing.Order
  340. m.filterRules[i] = r
  341. found = true
  342. break
  343. }
  344. }
  345. if !found {
  346. r.Order = len(m.filterRules) + 1
  347. m.filterRules = append(m.filterRules, r)
  348. }
  349. return m.commitChange()
  350. }
  351. func (m *FirewallManager) DeleteFilterRule(id string) error {
  352. m.mu.Lock()
  353. defer m.mu.Unlock()
  354. var updated []FilterRule
  355. for _, r := range m.filterRules {
  356. if r.ID != id {
  357. updated = append(updated, r)
  358. }
  359. }
  360. for i := range updated {
  361. updated[i].Order = i + 1
  362. }
  363. m.filterRules = updated
  364. return m.commitChange()
  365. }
  366. func (m *FirewallManager) MoveFilterRule(id string, dir string) error {
  367. m.mu.Lock()
  368. defer m.mu.Unlock()
  369. idx := -1
  370. for i, r := range m.filterRules {
  371. if r.ID == id {
  372. idx = i
  373. break
  374. }
  375. }
  376. if idx == -1 {
  377. return fmt.Errorf("rule not found")
  378. }
  379. if dir == "up" && idx > 0 {
  380. m.filterRules[idx], m.filterRules[idx-1] = m.filterRules[idx-1], m.filterRules[idx]
  381. } else if dir == "down" && idx < len(m.filterRules)-1 {
  382. m.filterRules[idx], m.filterRules[idx+1] = m.filterRules[idx+1], m.filterRules[idx]
  383. }
  384. for i := range m.filterRules {
  385. m.filterRules[i].Order = i + 1
  386. }
  387. return m.commitChange()
  388. }
  389. func (m *FirewallManager) SetFilterRuleState(id string, disabled bool) error {
  390. m.mu.Lock()
  391. defer m.mu.Unlock()
  392. for i := range m.filterRules {
  393. if m.filterRules[i].ID == id {
  394. m.filterRules[i].Disabled = disabled
  395. return m.commitChange()
  396. }
  397. }
  398. return fmt.Errorf("rule not found")
  399. }
  400. // ==================== NAT Rules ====================
  401. func (m *FirewallManager) ListNATRules() []NATRule {
  402. m.mu.Lock()
  403. defer m.mu.Unlock()
  404. counters, _ := ReadRuleCounters(m.filterRules, m.natRules, m.mangleRules)
  405. res := make([]NATRule, len(m.natRules))
  406. for i, r := range m.natRules {
  407. res[i] = r
  408. if c, ok := counters[r.ID]; ok {
  409. res[i].Packets = c[0]
  410. res[i].Bytes = c[1]
  411. }
  412. }
  413. return res
  414. }
  415. func (m *FirewallManager) SaveNATRule(r NATRule) error {
  416. m.mu.Lock()
  417. defer m.mu.Unlock()
  418. if err := validateChainPlacement("nat", r); err != nil {
  419. return err
  420. }
  421. if r.ID == "" {
  422. r.ID = fmt.Sprintf("nat-%d", time.Now().UnixNano())
  423. }
  424. found := false
  425. for i, existing := range m.natRules {
  426. if existing.ID == r.ID {
  427. r.Order = existing.Order
  428. m.natRules[i] = r
  429. found = true
  430. break
  431. }
  432. }
  433. if !found {
  434. r.Order = len(m.natRules) + 1
  435. m.natRules = append(m.natRules, r)
  436. }
  437. return m.commitChange()
  438. }
  439. func (m *FirewallManager) DeleteNATRule(id string) error {
  440. m.mu.Lock()
  441. defer m.mu.Unlock()
  442. var updated []NATRule
  443. for _, r := range m.natRules {
  444. if r.ID != id {
  445. updated = append(updated, r)
  446. }
  447. }
  448. for i := range updated {
  449. updated[i].Order = i + 1
  450. }
  451. m.natRules = updated
  452. return m.commitChange()
  453. }
  454. func (m *FirewallManager) MoveNATRule(id string, dir string) error {
  455. m.mu.Lock()
  456. defer m.mu.Unlock()
  457. idx := -1
  458. for i, r := range m.natRules {
  459. if r.ID == id {
  460. idx = i
  461. break
  462. }
  463. }
  464. if idx == -1 {
  465. return fmt.Errorf("rule not found")
  466. }
  467. if dir == "up" && idx > 0 {
  468. m.natRules[idx], m.natRules[idx-1] = m.natRules[idx-1], m.natRules[idx]
  469. } else if dir == "down" && idx < len(m.natRules)-1 {
  470. m.natRules[idx], m.natRules[idx+1] = m.natRules[idx+1], m.natRules[idx]
  471. }
  472. for i := range m.natRules {
  473. m.natRules[i].Order = i + 1
  474. }
  475. return m.commitChange()
  476. }
  477. func (m *FirewallManager) SetNATRuleState(id string, disabled bool) error {
  478. m.mu.Lock()
  479. defer m.mu.Unlock()
  480. for i := range m.natRules {
  481. if m.natRules[i].ID == id {
  482. m.natRules[i].Disabled = disabled
  483. return m.commitChange()
  484. }
  485. }
  486. return fmt.Errorf("rule not found")
  487. }
  488. // ==================== Mangle Rules ====================
  489. func (m *FirewallManager) ListMangleRules() []MangleRule {
  490. m.mu.Lock()
  491. defer m.mu.Unlock()
  492. counters, _ := ReadRuleCounters(m.filterRules, m.natRules, m.mangleRules)
  493. res := make([]MangleRule, len(m.mangleRules))
  494. for i, r := range m.mangleRules {
  495. res[i] = r
  496. if c, ok := counters[r.ID]; ok {
  497. res[i].Packets = c[0]
  498. res[i].Bytes = c[1]
  499. }
  500. }
  501. return res
  502. }
  503. func (m *FirewallManager) SaveMangleRule(r MangleRule) error {
  504. m.mu.Lock()
  505. defer m.mu.Unlock()
  506. if err := validateChainPlacement("mangle", r); err != nil {
  507. return err
  508. }
  509. if r.ID == "" {
  510. r.ID = fmt.Sprintf("mangle-%d", time.Now().UnixNano())
  511. }
  512. found := false
  513. for i, existing := range m.mangleRules {
  514. if existing.ID == r.ID {
  515. r.Order = existing.Order
  516. m.mangleRules[i] = r
  517. found = true
  518. break
  519. }
  520. }
  521. if !found {
  522. r.Order = len(m.mangleRules) + 1
  523. m.mangleRules = append(m.mangleRules, r)
  524. }
  525. return m.commitChange()
  526. }
  527. func (m *FirewallManager) DeleteMangleRule(id string) error {
  528. m.mu.Lock()
  529. defer m.mu.Unlock()
  530. var updated []MangleRule
  531. for _, r := range m.mangleRules {
  532. if r.ID != id {
  533. updated = append(updated, r)
  534. }
  535. }
  536. for i := range updated {
  537. updated[i].Order = i + 1
  538. }
  539. m.mangleRules = updated
  540. return m.commitChange()
  541. }
  542. func (m *FirewallManager) MoveMangleRule(id string, dir string) error {
  543. m.mu.Lock()
  544. defer m.mu.Unlock()
  545. idx := -1
  546. for i, r := range m.mangleRules {
  547. if r.ID == id {
  548. idx = i
  549. break
  550. }
  551. }
  552. if idx == -1 {
  553. return fmt.Errorf("rule not found")
  554. }
  555. if dir == "up" && idx > 0 {
  556. m.mangleRules[idx], m.mangleRules[idx-1] = m.mangleRules[idx-1], m.mangleRules[idx]
  557. } else if dir == "down" && idx < len(m.mangleRules)-1 {
  558. m.mangleRules[idx], m.mangleRules[idx+1] = m.mangleRules[idx+1], m.mangleRules[idx]
  559. }
  560. for i := range m.mangleRules {
  561. m.mangleRules[i].Order = i + 1
  562. }
  563. return m.commitChange()
  564. }
  565. func (m *FirewallManager) SetMangleRuleState(id string, disabled bool) error {
  566. m.mu.Lock()
  567. defer m.mu.Unlock()
  568. for i := range m.mangleRules {
  569. if m.mangleRules[i].ID == id {
  570. m.mangleRules[i].Disabled = disabled
  571. return m.commitChange()
  572. }
  573. }
  574. return fmt.Errorf("rule not found")
  575. }
  576. // ==================== Address Lists ====================
  577. func (m *FirewallManager) ListAddressLists() []AddressListEntry {
  578. m.mu.Lock()
  579. defer m.mu.Unlock()
  580. res := make([]AddressListEntry, len(m.addressLists))
  581. copy(res, m.addressLists)
  582. return append(res, m.dynamicEntries()...)
  583. }
  584. func (m *FirewallManager) SaveAddressListEntry(e AddressListEntry) error {
  585. if e.Dynamic || e.Timeout != 0 {
  586. return fmt.Errorf("dynamic entries are managed by packet rules")
  587. }
  588. m.mu.Lock()
  589. defer m.mu.Unlock()
  590. if e.ID == "" {
  591. e.ID = fmt.Sprintf("addrlist-%d", time.Now().UnixNano())
  592. }
  593. found := false
  594. for i, existing := range m.addressLists {
  595. if existing.ID == e.ID {
  596. m.addressLists[i] = e
  597. found = true
  598. break
  599. }
  600. }
  601. if !found {
  602. m.addressLists = append(m.addressLists, e)
  603. }
  604. return m.commitChange()
  605. }
  606. func (m *FirewallManager) DeleteAddressListEntry(id string) error {
  607. if strings.HasPrefix(id, "dynamic-") {
  608. m.mu.Lock()
  609. defer m.mu.Unlock()
  610. return deleteDynamicEntry(id)
  611. }
  612. m.mu.Lock()
  613. defer m.mu.Unlock()
  614. var updated []AddressListEntry
  615. for _, e := range m.addressLists {
  616. if e.ID != id {
  617. updated = append(updated, e)
  618. }
  619. }
  620. m.addressLists = updated
  621. return m.commitChange()
  622. }
  623. func (m *FirewallManager) SetAddressListEntryState(id string, disabled bool) error {
  624. m.mu.Lock()
  625. defer m.mu.Unlock()
  626. for i := range m.addressLists {
  627. if m.addressLists[i].ID == id {
  628. m.addressLists[i].Disabled = disabled
  629. return m.commitChange()
  630. }
  631. }
  632. return fmt.Errorf("address list entry not found")
  633. }
  634. func (m *FirewallManager) ResetCounters() error {
  635. m.mu.Lock()
  636. defer m.mu.Unlock()
  637. for _, binary := range []string{"iptables", "ip6tables"} {
  638. out, err := exec.Command(binary + "-save").Output()
  639. if err != nil {
  640. if binary == "iptables" {
  641. return err
  642. }
  643. continue
  644. }
  645. table := ""
  646. for _, line := range strings.Split(string(out), "\n") {
  647. if strings.HasPrefix(line, "*") {
  648. table = line[1:]
  649. }
  650. if strings.HasPrefix(line, ":DT_") {
  651. chain := strings.Fields(line)[0][1:]
  652. if err := exec.Command(binary, "-t", table, "-Z", chain).Run(); err != nil {
  653. return err
  654. }
  655. }
  656. }
  657. }
  658. return nil
  659. }
  660. func (m *FirewallManager) GetSummary() FirewallSummary {
  661. m.mu.Lock()
  662. defer m.mu.Unlock()
  663. conns, connErr := ListActiveConnections()
  664. connectionError := ""
  665. if connErr != nil {
  666. connectionError = connErr.Error()
  667. }
  668. geoCount := 0
  669. geoSubnets := 0
  670. if m.geoIPMgr != nil {
  671. status := m.geoIPMgr.GetStatus()
  672. if c, ok := status["enabled_count"].(int); ok {
  673. geoCount = c
  674. }
  675. if s, ok := status["total_subnets"].(int); ok {
  676. geoSubnets = s
  677. }
  678. }
  679. return FirewallSummary{
  680. FilterRulesCount: len(m.filterRules),
  681. NATRulesCount: len(m.natRules),
  682. MangleRulesCount: len(m.mangleRules),
  683. AddressListsCount: len(m.addressLists),
  684. GeoCountriesCount: geoCount,
  685. GeoSubnetsCount: geoSubnets,
  686. ActiveConnections: len(conns),
  687. ConnectionsError: connectionError,
  688. Applied: m.applied, LastError: m.lastError,
  689. }
  690. }
  691. // RenameInterface updates firewall rule interface names when an interface is renamed.
  692. func (m *FirewallManager) RenameInterface(oldName, newName string) {
  693. m.mu.Lock()
  694. defer m.mu.Unlock()
  695. changed := false
  696. for i := range m.filterRules {
  697. if m.filterRules[i].InInterface == oldName {
  698. m.filterRules[i].InInterface = newName
  699. changed = true
  700. }
  701. if m.filterRules[i].OutInterface == oldName {
  702. m.filterRules[i].OutInterface = newName
  703. changed = true
  704. }
  705. }
  706. for i := range m.natRules {
  707. if m.natRules[i].InInterface == oldName {
  708. m.natRules[i].InInterface = newName
  709. changed = true
  710. }
  711. if m.natRules[i].OutInterface == oldName {
  712. m.natRules[i].OutInterface = newName
  713. changed = true
  714. }
  715. }
  716. for i := range m.mangleRules {
  717. if m.mangleRules[i].InInterface == oldName {
  718. m.mangleRules[i].InInterface = newName
  719. changed = true
  720. }
  721. if m.mangleRules[i].OutInterface == oldName {
  722. m.mangleRules[i].OutInterface = newName
  723. changed = true
  724. }
  725. }
  726. if changed {
  727. if err := m.commitChange(); err != nil {
  728. m.lastError = err.Error()
  729. log.Printf("Firewall rename failed: %v", err)
  730. }
  731. }
  732. }