manager.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  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 r.ID == "" {
  331. r.ID = fmt.Sprintf("filter-%d", time.Now().UnixNano())
  332. }
  333. found := false
  334. for i, existing := range m.filterRules {
  335. if existing.ID == r.ID {
  336. r.Order = existing.Order
  337. m.filterRules[i] = r
  338. found = true
  339. break
  340. }
  341. }
  342. if !found {
  343. r.Order = len(m.filterRules) + 1
  344. m.filterRules = append(m.filterRules, r)
  345. }
  346. return m.commitChange()
  347. }
  348. func (m *FirewallManager) DeleteFilterRule(id string) error {
  349. m.mu.Lock()
  350. defer m.mu.Unlock()
  351. var updated []FilterRule
  352. for _, r := range m.filterRules {
  353. if r.ID != id {
  354. updated = append(updated, r)
  355. }
  356. }
  357. for i := range updated {
  358. updated[i].Order = i + 1
  359. }
  360. m.filterRules = updated
  361. return m.commitChange()
  362. }
  363. func (m *FirewallManager) MoveFilterRule(id string, dir string) error {
  364. m.mu.Lock()
  365. defer m.mu.Unlock()
  366. idx := -1
  367. for i, r := range m.filterRules {
  368. if r.ID == id {
  369. idx = i
  370. break
  371. }
  372. }
  373. if idx == -1 {
  374. return fmt.Errorf("rule not found")
  375. }
  376. if dir == "up" && idx > 0 {
  377. m.filterRules[idx], m.filterRules[idx-1] = m.filterRules[idx-1], m.filterRules[idx]
  378. } else if dir == "down" && idx < len(m.filterRules)-1 {
  379. m.filterRules[idx], m.filterRules[idx+1] = m.filterRules[idx+1], m.filterRules[idx]
  380. }
  381. for i := range m.filterRules {
  382. m.filterRules[i].Order = i + 1
  383. }
  384. return m.commitChange()
  385. }
  386. func (m *FirewallManager) SetFilterRuleState(id string, disabled bool) error {
  387. m.mu.Lock()
  388. defer m.mu.Unlock()
  389. for i := range m.filterRules {
  390. if m.filterRules[i].ID == id {
  391. m.filterRules[i].Disabled = disabled
  392. return m.commitChange()
  393. }
  394. }
  395. return fmt.Errorf("rule not found")
  396. }
  397. // ==================== NAT Rules ====================
  398. func (m *FirewallManager) ListNATRules() []NATRule {
  399. m.mu.Lock()
  400. defer m.mu.Unlock()
  401. counters, _ := ReadRuleCounters(m.filterRules, m.natRules, m.mangleRules)
  402. res := make([]NATRule, len(m.natRules))
  403. for i, r := range m.natRules {
  404. res[i] = r
  405. if c, ok := counters[r.ID]; ok {
  406. res[i].Packets = c[0]
  407. res[i].Bytes = c[1]
  408. }
  409. }
  410. return res
  411. }
  412. func (m *FirewallManager) SaveNATRule(r NATRule) error {
  413. m.mu.Lock()
  414. defer m.mu.Unlock()
  415. if r.ID == "" {
  416. r.ID = fmt.Sprintf("nat-%d", time.Now().UnixNano())
  417. }
  418. found := false
  419. for i, existing := range m.natRules {
  420. if existing.ID == r.ID {
  421. r.Order = existing.Order
  422. m.natRules[i] = r
  423. found = true
  424. break
  425. }
  426. }
  427. if !found {
  428. r.Order = len(m.natRules) + 1
  429. m.natRules = append(m.natRules, r)
  430. }
  431. return m.commitChange()
  432. }
  433. func (m *FirewallManager) DeleteNATRule(id string) error {
  434. m.mu.Lock()
  435. defer m.mu.Unlock()
  436. var updated []NATRule
  437. for _, r := range m.natRules {
  438. if r.ID != id {
  439. updated = append(updated, r)
  440. }
  441. }
  442. for i := range updated {
  443. updated[i].Order = i + 1
  444. }
  445. m.natRules = updated
  446. return m.commitChange()
  447. }
  448. func (m *FirewallManager) MoveNATRule(id string, dir string) error {
  449. m.mu.Lock()
  450. defer m.mu.Unlock()
  451. idx := -1
  452. for i, r := range m.natRules {
  453. if r.ID == id {
  454. idx = i
  455. break
  456. }
  457. }
  458. if idx == -1 {
  459. return fmt.Errorf("rule not found")
  460. }
  461. if dir == "up" && idx > 0 {
  462. m.natRules[idx], m.natRules[idx-1] = m.natRules[idx-1], m.natRules[idx]
  463. } else if dir == "down" && idx < len(m.natRules)-1 {
  464. m.natRules[idx], m.natRules[idx+1] = m.natRules[idx+1], m.natRules[idx]
  465. }
  466. for i := range m.natRules {
  467. m.natRules[i].Order = i + 1
  468. }
  469. return m.commitChange()
  470. }
  471. func (m *FirewallManager) SetNATRuleState(id string, disabled bool) error {
  472. m.mu.Lock()
  473. defer m.mu.Unlock()
  474. for i := range m.natRules {
  475. if m.natRules[i].ID == id {
  476. m.natRules[i].Disabled = disabled
  477. return m.commitChange()
  478. }
  479. }
  480. return fmt.Errorf("rule not found")
  481. }
  482. // ==================== Mangle Rules ====================
  483. func (m *FirewallManager) ListMangleRules() []MangleRule {
  484. m.mu.Lock()
  485. defer m.mu.Unlock()
  486. counters, _ := ReadRuleCounters(m.filterRules, m.natRules, m.mangleRules)
  487. res := make([]MangleRule, len(m.mangleRules))
  488. for i, r := range m.mangleRules {
  489. res[i] = r
  490. if c, ok := counters[r.ID]; ok {
  491. res[i].Packets = c[0]
  492. res[i].Bytes = c[1]
  493. }
  494. }
  495. return res
  496. }
  497. func (m *FirewallManager) SaveMangleRule(r MangleRule) error {
  498. m.mu.Lock()
  499. defer m.mu.Unlock()
  500. if r.ID == "" {
  501. r.ID = fmt.Sprintf("mangle-%d", time.Now().UnixNano())
  502. }
  503. found := false
  504. for i, existing := range m.mangleRules {
  505. if existing.ID == r.ID {
  506. r.Order = existing.Order
  507. m.mangleRules[i] = r
  508. found = true
  509. break
  510. }
  511. }
  512. if !found {
  513. r.Order = len(m.mangleRules) + 1
  514. m.mangleRules = append(m.mangleRules, r)
  515. }
  516. return m.commitChange()
  517. }
  518. func (m *FirewallManager) DeleteMangleRule(id string) error {
  519. m.mu.Lock()
  520. defer m.mu.Unlock()
  521. var updated []MangleRule
  522. for _, r := range m.mangleRules {
  523. if r.ID != id {
  524. updated = append(updated, r)
  525. }
  526. }
  527. for i := range updated {
  528. updated[i].Order = i + 1
  529. }
  530. m.mangleRules = updated
  531. return m.commitChange()
  532. }
  533. func (m *FirewallManager) MoveMangleRule(id string, dir string) error {
  534. m.mu.Lock()
  535. defer m.mu.Unlock()
  536. idx := -1
  537. for i, r := range m.mangleRules {
  538. if r.ID == id {
  539. idx = i
  540. break
  541. }
  542. }
  543. if idx == -1 {
  544. return fmt.Errorf("rule not found")
  545. }
  546. if dir == "up" && idx > 0 {
  547. m.mangleRules[idx], m.mangleRules[idx-1] = m.mangleRules[idx-1], m.mangleRules[idx]
  548. } else if dir == "down" && idx < len(m.mangleRules)-1 {
  549. m.mangleRules[idx], m.mangleRules[idx+1] = m.mangleRules[idx+1], m.mangleRules[idx]
  550. }
  551. for i := range m.mangleRules {
  552. m.mangleRules[i].Order = i + 1
  553. }
  554. return m.commitChange()
  555. }
  556. func (m *FirewallManager) SetMangleRuleState(id string, disabled bool) error {
  557. m.mu.Lock()
  558. defer m.mu.Unlock()
  559. for i := range m.mangleRules {
  560. if m.mangleRules[i].ID == id {
  561. m.mangleRules[i].Disabled = disabled
  562. return m.commitChange()
  563. }
  564. }
  565. return fmt.Errorf("rule not found")
  566. }
  567. // ==================== Address Lists ====================
  568. func (m *FirewallManager) ListAddressLists() []AddressListEntry {
  569. m.mu.Lock()
  570. defer m.mu.Unlock()
  571. res := make([]AddressListEntry, len(m.addressLists))
  572. copy(res, m.addressLists)
  573. return append(res, m.dynamicEntries()...)
  574. }
  575. func (m *FirewallManager) SaveAddressListEntry(e AddressListEntry) error {
  576. if e.Dynamic || e.Timeout != 0 {
  577. return fmt.Errorf("dynamic entries are managed by packet rules")
  578. }
  579. m.mu.Lock()
  580. defer m.mu.Unlock()
  581. if e.ID == "" {
  582. e.ID = fmt.Sprintf("addrlist-%d", time.Now().UnixNano())
  583. }
  584. found := false
  585. for i, existing := range m.addressLists {
  586. if existing.ID == e.ID {
  587. m.addressLists[i] = e
  588. found = true
  589. break
  590. }
  591. }
  592. if !found {
  593. m.addressLists = append(m.addressLists, e)
  594. }
  595. return m.commitChange()
  596. }
  597. func (m *FirewallManager) DeleteAddressListEntry(id string) error {
  598. if strings.HasPrefix(id, "dynamic-") {
  599. m.mu.Lock()
  600. defer m.mu.Unlock()
  601. return deleteDynamicEntry(id)
  602. }
  603. m.mu.Lock()
  604. defer m.mu.Unlock()
  605. var updated []AddressListEntry
  606. for _, e := range m.addressLists {
  607. if e.ID != id {
  608. updated = append(updated, e)
  609. }
  610. }
  611. m.addressLists = updated
  612. return m.commitChange()
  613. }
  614. func (m *FirewallManager) SetAddressListEntryState(id string, disabled bool) error {
  615. m.mu.Lock()
  616. defer m.mu.Unlock()
  617. for i := range m.addressLists {
  618. if m.addressLists[i].ID == id {
  619. m.addressLists[i].Disabled = disabled
  620. return m.commitChange()
  621. }
  622. }
  623. return fmt.Errorf("address list entry not found")
  624. }
  625. func (m *FirewallManager) ResetCounters() error {
  626. m.mu.Lock()
  627. defer m.mu.Unlock()
  628. for _, binary := range []string{"iptables", "ip6tables"} {
  629. out, err := exec.Command(binary + "-save").Output()
  630. if err != nil {
  631. if binary == "iptables" {
  632. return err
  633. }
  634. continue
  635. }
  636. table := ""
  637. for _, line := range strings.Split(string(out), "\n") {
  638. if strings.HasPrefix(line, "*") {
  639. table = line[1:]
  640. }
  641. if strings.HasPrefix(line, ":DT_") {
  642. chain := strings.Fields(line)[0][1:]
  643. if err := exec.Command(binary, "-t", table, "-Z", chain).Run(); err != nil {
  644. return err
  645. }
  646. }
  647. }
  648. }
  649. return nil
  650. }
  651. func (m *FirewallManager) GetSummary() FirewallSummary {
  652. m.mu.Lock()
  653. defer m.mu.Unlock()
  654. conns, connErr := ListActiveConnections()
  655. connectionError := ""
  656. if connErr != nil {
  657. connectionError = connErr.Error()
  658. }
  659. geoCount := 0
  660. geoSubnets := 0
  661. if m.geoIPMgr != nil {
  662. status := m.geoIPMgr.GetStatus()
  663. if c, ok := status["enabled_count"].(int); ok {
  664. geoCount = c
  665. }
  666. if s, ok := status["total_subnets"].(int); ok {
  667. geoSubnets = s
  668. }
  669. }
  670. return FirewallSummary{
  671. FilterRulesCount: len(m.filterRules),
  672. NATRulesCount: len(m.natRules),
  673. MangleRulesCount: len(m.mangleRules),
  674. AddressListsCount: len(m.addressLists),
  675. GeoCountriesCount: geoCount,
  676. GeoSubnetsCount: geoSubnets,
  677. ActiveConnections: len(conns),
  678. ConnectionsError: connectionError,
  679. Applied: m.applied, LastError: m.lastError,
  680. }
  681. }
  682. // RenameInterface updates firewall rule interface names when an interface is renamed.
  683. func (m *FirewallManager) RenameInterface(oldName, newName string) {
  684. m.mu.Lock()
  685. defer m.mu.Unlock()
  686. changed := false
  687. for i := range m.filterRules {
  688. if m.filterRules[i].InInterface == oldName {
  689. m.filterRules[i].InInterface = newName
  690. changed = true
  691. }
  692. if m.filterRules[i].OutInterface == oldName {
  693. m.filterRules[i].OutInterface = newName
  694. changed = true
  695. }
  696. }
  697. for i := range m.natRules {
  698. if m.natRules[i].InInterface == oldName {
  699. m.natRules[i].InInterface = newName
  700. changed = true
  701. }
  702. if m.natRules[i].OutInterface == oldName {
  703. m.natRules[i].OutInterface = newName
  704. changed = true
  705. }
  706. }
  707. for i := range m.mangleRules {
  708. if m.mangleRules[i].InInterface == oldName {
  709. m.mangleRules[i].InInterface = newName
  710. changed = true
  711. }
  712. if m.mangleRules[i].OutInterface == oldName {
  713. m.mangleRules[i].OutInterface = newName
  714. changed = true
  715. }
  716. }
  717. if changed {
  718. if err := m.commitChange(); err != nil {
  719. m.lastError = err.Error()
  720. log.Printf("Firewall rename failed: %v", err)
  721. }
  722. }
  723. }